Wie macht Pythons "Super" das Richtige?
-
03-07-2019 - |
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.
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 -
- Super ist eine integrierte Funktion, kein Attribut.
- Jeder Typ (Klasse) in Python hat eine
__mro__
Attribut, das die Methodenauflösungreihenfolge dieser bestimmten Instanz speichert. - 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.
- Am Startpunkt der Superaufrufe befindet sich das Objekt der Art der abgeleiteten Klasse (Sag DC).
- 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). - 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. - Spülenwäsche Wiederholung, bis alle Methoden gefunden und aufgerufen werden.
Erläuterung für Ihr Beispiel
MRO: D,B,C,A,object
super(D, self).__init__()
wird genannt. Issinstance (self, d) => wahrSuchen nach Nächste Methode in der MRO in Klassen rechts von D.
B.__init__
gefunden und gerufen
B.__init__
Anrufesuper(B, self).__init__()
.Issinstance (self, b) => falsch
Issinstance (self, d) => wahrSomit 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.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.