Frage

Ich leite Python 2.5 aus, daher gilt diese Frage möglicherweise nicht für Python 3. Wenn Sie eine Diamantklassenhierarchie mit mehreren Vererbung erstellen und ein Objekt der abgeleiteten Klasse erstellen, tut Python das Richtige (TM). Es nennt den Konstruktor für die abgeleitete Klasse, dann die übergeordneten Klassen von links nach rechts und dann der Großelternteil. Ich bin mit Pythons vertraut Mro; Das ist nicht meine Frage. Ich bin gespannt, wie das von Super zurückgegebene Objekt tatsächlich schafft, in den übergeordneten Klassen die richtige Reihenfolge zu kommunizieren. Betrachten Sie diesen Beispielcode:

#!/usr/bin/python

class A(object):
    def __init__(self): print "A init"

class B(A):
    def __init__(self):
        print "B init"
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print "C init"
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print "D init"
        super(D, self).__init__()

x = D()

Der Code macht das intuitive Ding, er druckt:

D init
B init
C init
A init

Wenn Sie jedoch den Aufruf an Super in Bs Init -Funktion kommentieren, wird weder A noch Cs Init -Funktion aufgerufen. Dies bedeutet, dass Bs Aufruf zum Super auf die Existenz von C in der Gesamtklassenhierarchie auf irgendwie bewusst ist. Ich weiß, dass Super ein Proxy -Objekt mit einem überlasteten Get -Operator zurückgibt, aber wie wird das von Super in Ds Init -Definition zurückgegebene Objekt die Existenz von C mit dem von Super in Bs Init -Definition zurückgegebenen Objekt vermittelt? Sind die Informationen, die nachfolgende Aufrufe von Super verwenden, die auf dem Objekt selbst gespeichert sind? Wenn ja, warum ist nicht super self. super nicht?

EDIT: Jekke hat zu Recht darauf hingewiesen, dass es nicht Selbstvertrauen ist, weil Super ein Attribut der Klasse ist, keine Instanz der Klasse. Konzeptionell ist dies sinnvoll, aber in der Praxis ist Super auch kein Attribut der Klasse! Sie können dies im Dolmetscher testen, indem Sie zwei Klassen A und B erstellen, wobei B von A erbt und anruft dir(B). Es hat keine super oder __super__ Attribute.

War es hilfreich?

Lösung

Ich habe unten eine Reihe von Links zur Verfügung gestellt, die Ihre Frage genauer beantworten und genauer gesagt, als ich jemals hoffen kann. Ich werde jedoch auch in meinen eigenen Worten eine Antwort auf Ihre Frage geben, um Ihnen einige Zeit zu sparen. Ich werde es in Punkte stecken -

  1. Super ist eine integrierte Funktion, kein Attribut.
  2. Jeder Typ (Klasse) in Python hat eine __mro__ Attribut, das die Methodenauflösungreihenfolge dieser bestimmten Instanz speichert.
  3. Jeder Aufruf zum Super ist vom Formular Super (Typ [, Objekt-oder-Typ]). Nehmen wir an, das zweite Attribut ist für den Moment ein Objekt.
  4. Am Startpunkt der Superaufrufe befindet sich das Objekt der Art der abgeleiteten Klasse (Sag DC).
  5. Super sucht nach Methoden, die übereinstimmen (in Ihrem Fall __init__) in den Klassen in der MRO nach der Klasse als erstes Argument (in diesem Fall Klassen nach DC).
  6. Wenn die Matching -Methode gefunden wird (z. B. im Unterricht BC1), es wird genannt.
    (Diese Methode sollte Super verwenden, daher gehe ich davon aus BC1.
  7. Spülenwäsche Wiederholung, bis alle Methoden gefunden und aufgerufen werden.

Erläuterung für Ihr Beispiel

 MRO: D,B,C,A,object  
  1. super(D, self).__init__() wird genannt. Issinstance (self, d) => wahr
  2. Suchen nach Nächste Methode in der MRO in Klassen rechts von D.

    B.__init__ gefunden und gerufen


  1. B.__init__ Anrufe super(B, self).__init__().

    Issinstance (self, b) => falsch
    Issinstance (self, d) => wahr

  2. Somit ist die MRO dieselbe, aber die Suche setzt sich rechts von B ie C, A, Objekt weiter nacheinander durchsucht. Der nächste __init__ gefunden wird genannt.

  3. Und so weiter und so fort.

Eine Erklärung von Super
http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
Dinge, auf die man achten muss, wenn man Super benutzt
http://fuhm.net/super-harmful/
Pythons MRO -Algorithmus:
http://www.python.org/download/releases/2.3/mro/
Supers Docs:
http://docs.python.org/library/functions.html
Der Ende dieser Seite hat einen schönen Abschnitt über Super:
http://docstore.mik.ua/orelly/other/python/0596001886_pythonian-chp-5-sect-2.html

Ich hoffe, das hilft, es zu klären.

Andere Tipps

Ändern Sie Ihren Code darauf und ich denke, er wird Dinge erklären (vermutlich super schaut sich an, wo beispielsweise: B ist in dem __mro__?):

class A(object):
    def __init__(self):
        print "A init"
        print self.__class__.__mro__

class B(A):
    def __init__(self):
        print "B init"
        print self.__class__.__mro__
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print "C init"
        print self.__class__.__mro__
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print "D init"
        print self.__class__.__mro__
        super(D, self).__init__()

x = D()

Wenn Sie es ausführen, werden Sie sehen:

D init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
B init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
C init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
A init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)

Es lohnt sich auch, sich zu überprüfen Pythons Super ist raffiniert, aber Sie können es nicht verwenden.

einfach raten:

self In allen vier Methoden beziehen sich auf dasselbe Objekt, dh auf die Klasse D. Also in B.__init__(), der Anruf zu super(B,self) kennt den ganzen Diamantvorfahren von self und es muss die Methode von 'After' abrufen B. In diesem Fall ist es das C Klasse.

super() kennt das voll Klassenhierarchie. Das passiert in Bs Init:

>>> super(B, self)
<super: <class 'B'>, <D object>>

Dies löst die zentrale Frage,

Wie vermittelt das von Super in Ds Init -Definition zurückgegebene Objekt die Existenz von C mit dem von Super in Bs Init -Definition zurückgegebenen Objekt?

Nämlich in der Init -Definition von B, nämlich, self ist eine Instanz von D, und kommuniziert so die Existenz von C. Zum Beispiel C kann gefunden werden in type(self).__mro__.

Jacobs Antwort zeigt, wie man das Problem versteht, während Batbrats die Details und die HRRs direkt auf den Punkt gehen.

Eine Sache, die sie nicht (zumindest nicht der Experation) aus Ihrer Frage abdecken, ist dieser Punkt:

Wenn Sie jedoch den Aufruf an Super in Bs Init -Funktion kommentieren, wird weder A noch Cs Init -Funktion aufgerufen.

Um dies zu verstehen, ändern Sie den Code von Jacob in, um den Stapel auf A's Init wie unten zu drucken:

import traceback

class A(object):
    def __init__(self):
        print "A init"
        print self.__class__.__mro__
        traceback.print_stack()

class B(A):
    def __init__(self):
        print "B init"
        print self.__class__.__mro__
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print "C init"
        print self.__class__.__mro__
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print "D init"
        print self.__class__.__mro__
        super(D, self).__init__()

x = D()

Es ist ein bisschen überraschend, das zu sehen B's-Linie super(B, self).__init__() ruft tatsächlich an C.__init__(), wie C ist keine Basisklasse von B.

D init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
B init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
C init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
A init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
  File "/tmp/jacobs.py", line 31, in <module>
    x = D()
  File "/tmp/jacobs.py", line 29, in __init__
    super(D, self).__init__()
  File "/tmp/jacobs.py", line 17, in __init__
    super(B, self).__init__()
  File "/tmp/jacobs.py", line 23, in __init__
    super(C, self).__init__()
  File "/tmp/jacobs.py", line 11, in __init__
    traceback.print_stack()

Dies geschieht, weil super (B, self) ist nicht 'Rufen Sie die Baseclass -Version des B an von __init__'. Stattdessen ist es ''Berufung __init__ auf der ersten Klasse rechts von B das ist vorhanden auf self's __mro__ Und das hat ein solches Attribut.

Also wenn du kommentieren Sie den Anruf bei Super in Bs Init -Funktion, Der Method -Stack hört auf B.__init__, und werde niemals erreichen C oder A.

Zusammenfassen:

  • Unabhängig davon, welche Klasse sich darauf bezieht, self ist immer ein Hinweis auf die Instanz und ihre __mro__ und __class__ gleich bleiben
  • Super () findet die Methode, die auf die Klassen schaut, die rechts von der aktuellen auf dem stehen __mro__. Als die __mro__ bleibt konstant, was passiert ist, dass es als Liste gesucht wird, nicht als Baum oder Diagramm.

Beachten Sie in diesem letzten Punkt, dass der vollständige Name des MRO -Algorithmus lautet C3 Superclass -Linearisierung. Das heißt, es flacht diese Struktur in eine Liste. Wenn die verschiedenen super() Anrufe passieren, sie iterieren diese Liste effektiv.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top