Question

class A(object):
    def __init__(self, a, b, c):
        #super(A, self).__init__()
        super(self.__class__, self).__init__()


class B(A):
    def __init__(self, b, c):
        print super(B, self)
        print super(self.__class__, self)
        #super(B, self).__init__(1, b, c)
        super(self.__class__, self).__init__(1, b, c)

class C(B):
    def __init__(self, c):
        #super(C, self).__init__(2, c)
        super(self.__class__, self).__init__(2, c)
C(3)

Dans le code ci-dessus, la mise en commentaire des appels __init__ semblent l'être communément admise façon « intelligente » pour faire l'initialisation super classe. Toutefois, dans le cas où la hiérarchie des classes est susceptible de changer, j'utilise la forme décommentée, jusqu'à récemment.

Il semble que dans l'appel au super constructeur pour B dans la hiérarchie ci-dessus, que B.__init__ est appelé à nouveau, self.__class__ est en fait C, pas B comme je l'avais toujours supposé.

Est-il possible en Python-2.x que je peux maintenir une bonne MRO (par rapport à l'initialisation de toutes les classes de parents dans le bon ordre) lorsque vous appelez super-constructeurs tout en ne nommant pas la classe actuelle (le B en en super(B, self).__init__(1, b, c))?

Était-ce utile?

La solution

Réponse courte:. Non, il n'y a aucun moyen d'invoquer implicitement le droit __init__ avec les bons arguments de la classe parente droit en Python 2.x

Par ailleurs, le code comme indiqué ici est incorrect:. Si vous utilisez super () __init__, toutes les classes de votre hiérarchie doit avoir la même signature dans leurs méthodes de __init__. Sinon, votre code peut cesser de fonctionner si vous introduisez une nouvelle sous-classe qui utilise l'héritage multiple.

Voir http://fuhm.net/super-harmful/ pour une description plus détaillée la question (avec des images).

Autres conseils

Votre code n'a rien à voir avec l'ordre de résolution de la méthode. Résolution de la méthode est dans le cas d'héritage multiple qui n'est pas le cas de votre exemple. Votre code est tout simplement faux parce que vous supposez que self.__class__ est en fait la même classe de celui où la méthode est définie et cela est faux:

>>> class A(object):
...     def __init__(self):
...         print self.__class__
... 
>>> 
>>> class B(A):
...     def __init__(self):
...         A.__init__(self)
... 
>>> B()
<class '__main__.B'>
<__main__.B object at 0x1bcfed0>
>>> A()
<class '__main__.A'>
<__main__.A object at 0x1bcff90>
>>> 

donc quand vous devez appeler:

super(B, self).__init__(1, b, c)

vous appelez en effet:

# super(self.__class__, self).__init__(1, b, c)
super(C, self).__init__(1, b, c)

EDIT :. Essayer de mieux répondre à la question

class A(object):
    def __init__(self, a):
        for cls in self.__class__.mro():
            if cls is not object:
                cls._init(self, a)
    def _init(self, a):
        print 'A._init'
        self.a = a

class B(A):
    def _init(self, a):
        print 'B._init'

class C(A):
    def _init(self, a):
        print 'C._init'

class D(B, C):
    def _init(self, a):
        print 'D._init'


d = D(3)
print d.a

impressions:

D._init
B._init
C._init
A._init
3

(Une version modifiée de href="http://en.wikipedia.org/wiki/Template_Method_Pattern" ).

Les méthodes des parents sont vraiment appelé implicitement, mais je suis d'accord avec le zen python où est explicite mieux que implicite car le code est moins lisible et le gain est faible. Mais méfiez-vous que toutes les méthodes de _init ont les mêmes paramètres, vous ne pouvez pas complètement oublier les parents et je ne suggère pas de le faire.

Pour héritage unique, une meilleure approche consiste à appeler explicitement la méthode des parents, sans invoquer super. Ce faisant, vous n'avez pas à nom de la classe actuelle , mais vous devez se soucier de qui est la classe du parent.

Bon lit sont: comment-t-pythons-super-do -le-droit chose et les liens suggérés dans cette question et particularité super Python est astucieux, mais vous ne pouvez pas l'utiliser

Si la hiérarchie est susceptible de changer est les symptômes d'une mauvaise conception et a des conséquences dans toutes les parties qui utilisent ce code et ne doit pas être encouragé.

EDIT 2

Un autre exemple me vient à l'esprit, mais qui utilise métaclasses. bibliothèque urwid utilise métaclasse pour stocker un attribut, __super, donc en classe que vous avez juste besoin d'accéder à cet attribut.

Ex:

>>> class MetaSuper(type):
...     """adding .__super"""
...     def __init__(cls, name, bases, d):
...         super(MetaSuper, cls).__init__(name, bases, d)
...         if hasattr(cls, "_%s__super" % name):
...             raise AttributeError, "Class has same name as one of its super classes"
...         setattr(cls, "_%s__super" % name, super(cls))
... 
>>> class A:
...  __metaclass__ = MetaSuper
...  def __init__(self, a):
...   self.a = a
...   print 'A.__init__'
... 
>>> class B(A):
...  def __init__(self, a):
...   print 'B.__init__'
...   self.__super.__init__(a)
... 
>>> b = B(42)
B.__init__
A.__init__
>>> b.a
42
>>> 

Peut-être ce que vous cherchez est métaclasses?

class metawrap(type):
    def __new__(mcs,name, bases, dict):
        dict['bases'] = bases
        return type.__new__(mcs,name,bases,dict)

class A(object):
    def __init__(self):
        pass
    def test(self):
        print "I am class A"

class B(A):
    __metaclass__ = metawrap
    def __init__(self):
        pass
    def test(self):
        par = super(self.bases[0],self)
        par.__thisclass__.test(self)
foo = B()
foo.test()

Imprime "Je suis classe A"

Ce que le métaclasse fait est primordial la création initiale de la classe B (et non l'objet) et veille à ce que le dictionnaire intégré pour chaque objet B contient maintenant un tableau des bases où vous pouvez trouver tous les baseclasses pour B

A ma connaissance, ce qui suit est généralement pas fait. Mais il ne semble pas fonctionner.

Méthodes dans une définition de classe donnée toujours Mutilation double underscore attribue à inclure le nom de la classe qu'ils sont définis. Donc, si vous planquer une référence à la classe sous forme de nom mutilées où les instances peuvent le voir, vous pouvez l'utiliser dans l'appel à super.

Un exemple stashing les références sur l'objet lui-même, en mettant en œuvre __new__ sur le baseclass:

def mangle(cls, name):
    if not name.startswith('__'):
        raise ValueError('name must start with double underscore')
    return '_%s%s' % (cls.__name__, name)

class ClassStasher(object):
    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls)
        for c in cls.mro():
            setattr(obj, mangle(c, '__class'), c)
        return obj

class A(ClassStasher):
    def __init__(self):
        print 'init in A', self.__class
        super(self.__class, self).__init__()

class B(A):
    def __init__(self):
        print 'init in B', self.__class
        super(self.__class, self).__init__()

class C(A):
    def __init__(self):
        print 'init in C', self.__class
        super(self.__class, self).__init__()

class D(B, C):
    def __init__(self):
        print 'init in D', self.__class
        super(self.__class, self).__init__()


d = D()    
print d

Et, faire quelque chose de semblable, mais en utilisant une méta-classe et stashing les références de __class sur la classe des objets eux-mêmes:

class ClassStasherType(type):
    def __init__(cls, name, bases, attributes):
        setattr(cls, mangle(cls, '__class'), cls)

class ClassStasher(object):
    __metaclass__ = ClassStasherType

class A_meta(ClassStasher):
    def __init__(self):
        print 'init in A_meta', self.__class
        super(self.__class, self).__init__()

class B_meta(A_meta):
    def __init__(self):
        print 'init in B_meta', self.__class
        super(self.__class, self).__init__()

class C_meta(A_meta):
    def __init__(self):
        print 'init in C_meta', self.__class
        super(self.__class, self).__init__()

class D_meta(B_meta, C_meta):
    def __init__(self):
        print 'init in D_meta', self.__class
        super(self.__class, self).__init__()


d = D_meta()    
print d

L'exécution de cette tous ensemble, comme un seul fichier source:

% python /tmp/junk.py
init in D <class '__main__.D'>
init in B <class '__main__.B'>
init in C <class '__main__.C'>
init in A <class '__main__.A'>
<__main__.D object at 0x1004a4a50>
init in D_meta <class '__main__.D_meta'>
init in B_meta <class '__main__.B_meta'>
init in C_meta <class '__main__.C_meta'>
init in A_meta <class '__main__.A_meta'>
<__main__.D_meta object at 0x1004a4bd0>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top