Frage

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)

In dem obigen Code, kommentierte der aus __init__ Anrufe an die sein landläufigen „intelligente“ Art und Weise erscheinen Superklasse Initialisierung zu tun. Doch in dem Fall, dass die Klassenhierarchie wird sich wahrscheinlich ändern, habe ich das unkommentiert Formular, bis vor kurzem.

Es scheint, dass in dem Aufruf das Super-Konstruktor für B in der obigen Hierarchie, dass B.__init__ erneut aufgerufen wird, self.__class__ ist eigentlich C, nicht B wie ich immer angenommen hatte.

Gibt es eine Möglichkeit in Python-2.x, dass ich die richtige MRO halten kann (bezüglich aller übergeordneten Klassen in der richtigen Reihenfolge zu initialisieren), wenn Super Konstrukteuren Aufruf während nicht die aktuellen Klasse (die B bei der Benennung in super(B, self).__init__(1, b, c))?

War es hilfreich?

Lösung

Kurze Antwort:. Nein, es gibt keinen Weg, um implizit invoke rechts __init__ mit den richtigen Argumenten von der rechten Seite übergeordneten Klasse in Python 2.x

Im Übrigen, wie der hier gezeigte Code ist falsch. Wenn man super () verwenden __init__, dann werden alle Klassen in der Hierarchie die gleiche Signatur in ihre __init__ Methoden haben müssen. Andernfalls können Sie den Code aufhören zu arbeiten, wenn Sie eine neue Unterklasse, dass Anwendungen Mehrfachvererbung einzuführen.

Siehe http://fuhm.net/super-harmful/ für eine längere Beschreibung die Ausgabe (mit Bildern).

Andere Tipps

Ihr Code hat nichts mit der Methode Auflösung, um zu tun. Verfahren Auflösung kommt im Fall von Mehrfachvererbung, die nicht der Fall Ihres Beispiels. Der Code ist einfach falsch, weil man davon ausgehen, dass self.__class__ ist eigentlich die gleiche Klasse von dem, in dem die Methode definiert ist, und das ist falsch:

>>> 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>
>>> 

so, wenn Sie anrufen sollen:

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

Sie sind in der Tat Aufruf:

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

Bearbeiten :. Die Frage besser beantworten versuchen

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

druckt:

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

(Eine modifizierte Version von Schablonenmuster ).

Jetzt Eltern Methoden sind wirklich implizit genannt, aber ich habe mit Python zen zustimmen, wo explizit ist besser als implizit, da der Code weniger lesbar ist und die Verstärkung ist schlecht. Aber Vorsicht, dass alle _init Methoden die gleichen Parameter haben, können Sie nicht vollständig über die Eltern vergessen und ich schlage vor, nicht so zu tun.

Für die einfache Vererbung, ein besserer Ansatz ist Methode der expliziten Aufruf Eltern, ohne super aufrufen. Dabei müssen Sie die aktuelle Klasse nicht name , aber immer noch müssen Sie kümmern, die Klasse der Eltern ist.

Good liest werden: wie-ist-Pythons-super-do -the-rechts-Sache und die in dieser Frage vorgeschlagen Links und in Besonderheit Python super ist geschickt, aber Sie können es nicht verwenden

Wenn Hierarchie wahrscheinlich zu ändern ist, um Symptome von schlechtem Design und hat Folgen in allen Teilen, die diesen Code verwenden und nicht gefördert werden soll.

EDIT 2

Ein weiteres Beispiel kommt mir im Kopf, aber die nutzt metaclasses. Urwid Bibliothek Anwendungen metaclass ein Attribut, __super zu speichern, in der Klasse so dass Sie brauchen nur um den Zugriff auf das Attribut.

Beispiel:

>>> 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
>>> 

Vielleicht, was Sie suchen ist metaclasses?

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()

Prints "Ich bin Klasse A"

Was die Metaklasse tut, ist die anfängliche Erstellung der B Klasse überschreiben (nicht das Objekt) und stellt sicher, dass das eingebaute Wörterbuch für jedes B-Objekt enthält nun eine Basen-Array, wo Sie alle Basisklassen für B finden

Mein Wissen wird die folgende nicht häufig getan. Aber es scheint zu arbeiten.

Methoden in einer bestimmten Klassendefinition immer mangle Doppelstrich Attribute den Namen der Klasse gehören sie in definiert sind. Also, wenn Sie einen Verweis auf die Klasse in Name-verstümmelten Form bunkern, wo die Instanzen es sehen können, Sie können die in dem Aufruf von super verwenden.

Ein Beispiel die Verweise auf das Objekt stashing selbst, durch __new__ auf der Basisklasse Umsetzung:

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

Und eine ähnliche Sache zu tun, aber mit einer Meta-Klasse und stashing die __class Referenzen auf die Klasse-Objekte selbst:

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

Ausführen dieses alle zusammen, als eine Quelldatei:

% 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>
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top