Was ist der Unterschied zwischen:
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
und:
class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
Ich habe gesehen, super
dass es in Klassen mit nur einer einzigen Vererbung ziemlich oft verwendet wird. Ich kann sehen, warum Sie es bei Mehrfachvererbung verwenden würden, bin mir jedoch nicht sicher, welche Vorteile die Verwendung in einer solchen Situation hat.
quelle
Was ist der Unterschied?
Mittel zum Aufruf
SomeBaseClass
‚s__init__
. währendbedeutet, eine Grenze
__init__
aus der übergeordneten Klasse aufzurufen , dieChild
in der Method Resolution Order (MRO) der Instanz folgt .Wenn die Instanz eine Unterklasse von Child ist, gibt es möglicherweise ein anderes übergeordnetes Element, das im MRO als Nächstes angezeigt wird.
Einfach erklärt
Wenn Sie eine Klasse schreiben, möchten Sie, dass andere Klassen sie verwenden können.
super()
erleichtert anderen Klassen die Verwendung der von Ihnen geschriebenen Klasse.Wie Bob Martin sagt, können Sie mit einer guten Architektur die Entscheidungsfindung so lange wie möglich verschieben.
super()
kann diese Art von Architektur ermöglichen.Wenn eine andere Klasse die von Ihnen geschriebene Klasse unterklassifiziert, kann sie auch von anderen Klassen erben. Und diese Klassen könnten eine haben
__init__
, die danach kommt,__init__
basierend auf der Reihenfolge der Klassen für die Methodenauflösung.Ohne
super
Sie würden Sie wahrscheinlich das übergeordnete Element der Klasse, die Sie schreiben, hart codieren (wie im Beispiel). Dies würde bedeuten, dass Sie den nächsten__init__
im MRO nicht aufrufen und somit den darin enthaltenen Code nicht wiederverwenden können.Wenn Sie Ihren eigenen Code für den persönlichen Gebrauch schreiben, ist Ihnen diese Unterscheidung möglicherweise egal. Wenn Sie jedoch möchten, dass andere Ihren Code verwenden, ist die Verwendung
super
eine Sache, die Benutzern des Codes eine größere Flexibilität ermöglicht.Python 2 gegen 3
Dies funktioniert in Python 2 und 3:
Dies funktioniert nur in Python 3:
Es funktioniert ohne Argumente, indem es im Stapelrahmen nach oben geht und das erste Argument für die Methode abruft (normalerweise
self
für eine Instanzmethode odercls
für eine Klassenmethode - kann aber auch andere Namen haben) und die Klasse (z. B.Child
) in den freien Variablen findet ( es wird mit dem Namen__class__
als freie Abschlussvariable in der Methode nachgeschlagen ).Ich bevorzuge es, die kompatible Verwendung zu demonstrieren
super
, aber wenn Sie nur Python 3 verwenden, können Sie es ohne Argumente aufrufen.Indirektion mit Vorwärtskompatibilität
Was gibt es dir? Bei der Einzelvererbung sind die Beispiele aus der Frage aus Sicht der statischen Analyse praktisch identisch. Durch die Verwendung erhalten
super
Sie jedoch eine Indirektionsebene mit Vorwärtskompatibilität.Vorwärtskompatibilität ist für erfahrene Entwickler sehr wichtig. Sie möchten, dass Ihr Code beim Ändern mit minimalen Änderungen weiterarbeitet. Wenn Sie sich Ihren Revisionsverlauf ansehen, möchten Sie genau sehen, was sich wann geändert hat.
Sie können mit einer einzelnen Vererbung beginnen, aber wenn Sie eine weitere Basisklasse hinzufügen möchten, müssen Sie nur die Zeile mit den Basen ändern. Wenn sich die Basen in einer Klasse ändern, von der Sie erben (z. B. wenn ein Mixin hinzugefügt wird), würden Sie dies ändern nichts in dieser Klasse. Insbesondere in Python 2
super
kann es schwierig sein , die Argumente und die richtigen Methodenargumente richtig zu finden. Wenn Sie wissen, dass Sie diesuper
Einzelvererbung korrekt verwenden, ist das Debuggen in Zukunft weniger schwierig.Abhängigkeitsspritze
Andere Personen können Ihren Code verwenden und Eltern in die Methodenauflösung einfügen:
Angenommen, Sie fügen Ihrem Objekt eine weitere Klasse hinzu und möchten eine Klasse zwischen Foo und Bar einfügen (zum Testen oder aus einem anderen Grund):
Wenn Sie das un-super-Kind verwenden, kann die Abhängigkeit nicht eingefügt werden, da das von Ihnen verwendete Kind die Methode, die nach seiner eigenen aufgerufen werden soll, fest codiert hat:
Die Klasse mit dem verwendeten Kind
super
kann die Abhängigkeit jedoch korrekt einfügen:Einen Kommentar adressieren
Python linearisiert einen komplizierten Vererbungsbaum über den C3-Linearisierungsalgorithmus , um eine MRO (Method Resolution Order) zu erstellen.
Wir möchten, dass die Methoden in dieser Reihenfolge nachgeschlagen werden .
Damit eine in einem Elternteil definierte Methode die nächste in dieser Reihenfolge ohne finden kann
super
, müsste dies der Fall seinDer
UnsuperChild
hat keinen Zugriff aufInjectMe
. Es ist dasUnsuperInjector
, das Zugriff aufInjectMe
die Methode dieser Klasse hat und diese dennoch nicht von der Methode aufrufen kann, von der es erbtUnsuperChild
.Beide untergeordneten Klassen beabsichtigen, eine Methode mit demselben Namen aufzurufen, die im MRO als Nächstes angezeigt wird. Dies kann eine andere Klasse sein , die bei der Erstellung nicht bekannt war.
Derjenige ohne
super
Hardcodierung der Methode seines übergeordneten Elements hat somit das Verhalten seiner Methode eingeschränkt, und Unterklassen können keine Funktionalität in die Aufrufkette einfügen.Der mit
super
hat eine größere Flexibilität. Die Aufrufkette für die Methoden kann abgefangen und Funktionen eingefügt werden.Möglicherweise benötigen Sie diese Funktionalität nicht, Unterklassen Ihres Codes jedoch möglicherweise.
Fazit
Verwenden Sie immer, um
super
auf die übergeordnete Klasse zu verweisen, anstatt sie fest zu codieren.Sie möchten auf die übergeordnete Klasse verweisen, die als nächstes in der Zeile steht, und nicht speziell auf die, von der das Kind erbt.
Wenn Sie nicht verwenden,
super
können die Benutzer Ihres Codes unnötige Einschränkungen erfahren.quelle
list
Schnittstelle hinzufüge, wird sie beispielsweise vondoublylinkedlist
der Anwendung reibungslos ausgewählt. Ich kann mein Beispiel konfigurierbarer machen, indem ich dieconfig.txt
Implementierung beim Laden einführe und verknüpfe. Ist das das richtige Beispiel? Wenn ja, wie beziehe ich Ihren Code? Siehe den ersten Adv von DI im Wiki. Wo ist eine neue Implementierung konfigurierbar? in Ihrem CodeInjectMe
Klasse erbt . Kommentare sind jedoch nicht zur Diskussion, daher schlage ich vor, dass Sie dies im Chat weiter mit anderen diskutieren oder auf der Hauptseite eine neue Frage stellen.__init__
Funktionen auf. insbesondere wenn die Signatur von__init__
zwischen Klassen in der Hierarchie variiert. Ich habe eine Antwort hinzugefügt, die sich auf diesen Aspekt konzentriertIch hatte ein bisschen mit gespielt
super()
und erkannt, dass wir die Anrufreihenfolge ändern können.Zum Beispiel haben wir die nächste Hierarchiestruktur:
In diesem Fall ist MRO von D (nur für Python 3):
Erstellen wir eine Klasse, in
super()
der nach der Ausführung der Methode aufgerufen wird.Wir können also sehen, dass die Auflösungsreihenfolge dieselbe ist wie in MRO. Aber wenn wir
super()
am Anfang der Methode aufrufen :Wir haben eine andere Reihenfolge, es ist eine umgekehrte Reihenfolge des MRO-Tupels.
Für zusätzliche Lektüre würde ich die nächsten Antworten empfehlen:
quelle
Geht das nicht alles davon aus, dass die Basisklasse eine Klasse neuen Stils ist?
Funktioniert nicht in Python 2.
class A
muss neu sein, dh:class A(object)
quelle
Wenn Sie aufrufen
super()
, um die Version einer Klassenmethode, Instanzmethode oder statischen Methode eines übergeordneten Elements aufzulösen, möchten wir die aktuelle Klasse, in deren Bereich wir uns befinden, als erstes Argument übergeben, um anzugeben, in welchen übergeordneten Bereich wir auflösen möchten und als Ein zweites Argument ist das interessierende Objekt, um anzugeben, auf welches Objekt wir diesen Bereich anwenden möchten.Betrachten wir eine Klassenhierarchie
A
,B
undC
wobei jede Klasse ist die Mutter des einen darauf folgenden, unda
,b
undc
jeweilige Instanzen der einzelnen.Verwenden
super
mit einer statischen MethodezB
super()
aus der__new__()
Methode heraus verwendenErläuterung:
1- obwohl es üblich ist für
__new__()
als erstes param einen Verweis auf die anrufenden Klasse zu nehmen, wird es nicht in Python als Class implementiert, sondern ein static. Das heißt, ein Verweis auf eine Klasse muss beim__new__()
direkten Aufruf explizit als erstes Argument übergeben werden:2- Wenn
super()
wir aufrufen , um zur übergeordneten Klasse zu gelangen, übergeben wir die untergeordnete KlasseA
als erstes Argument. Dann übergeben wir einen Verweis auf das interessierende Objekt. In diesem Fall ist es die Klassenreferenz, die beimA.__new__(cls)
Aufruf übergeben wurde. In den meisten Fällen handelt es sich auch um einen Verweis auf die untergeordnete Klasse. In einigen Situationen ist dies möglicherweise nicht der Fall, beispielsweise bei Vererbungen mit mehreren Generationen.3- Da es sich in der Regel
__new__()
um eine statische Methode handelt,super(A, cls).__new__
wird auch eine statische Methode zurückgegeben, und in diesem Fall müssen alle Argumente explizit angegeben werden, einschließlich des Verweises auf das Objekt von insterestcls
.4- das Gleiche tun ohne
super
Verwenden
super
mit einer InstanzmethodezB
super()
von innen benutzen__init__()
Erläuterung:
1-
__init__
ist eine Instanzmethode, dh sie verwendet als erstes Argument einen Verweis auf eine Instanz. Beim direkten Aufruf von der Instanz wird die Referenz implizit übergeben, dh Sie müssen sie nicht angeben:2- Wenn wir
super()
innerhalb aufrufen, übergeben__init__()
wir die untergeordnete Klasse als erstes Argument und das interessierende Objekt als zweites Argument, das im Allgemeinen auf eine Instanz der untergeordneten Klasse verweist.3- Der Aufruf
super(A, self)
gibt einen Proxy zurück, der den Bereich auflöst und auf ihn anwendet,self
als wäre er jetzt eine Instanz der übergeordneten Klasse. Nennen wir diesen Proxys
. Da__init__()
es sich um eine Instanzmethode handelt, übergibt der Aufrufs.__init__(...)
implizit einen Verweisself
als erstes Argument an den übergeordneten__init__()
.4- Um dasselbe zu tun, ohne dass
super
wir einen Verweis auf eine Instanz explizit an die übergeordnete Version von übergeben müssen__init__()
.Verwenden
super
mit einer KlassenmethodeErläuterung:
1- Eine Klassenmethode kann direkt von der Klasse aufgerufen werden und verwendet als ersten Parameter einen Verweis auf die Klasse.
2- Wenn Sie
super()
innerhalb einer Klassenmethode aufrufen, um sie in die übergeordnete Version aufzulösen, möchten wir die aktuelle untergeordnete Klasse als erstes Argument übergeben, um anzugeben, in welchen übergeordneten Bereich wir auflösen möchten, und das interessierende Objekt als zweites Argument um anzugeben, auf welches Objekt wir diesen Bereich anwenden möchten, der im Allgemeinen auf die untergeordnete Klasse selbst oder eine ihrer Unterklassen verweist.3- Der Anruf wird
super(B, cls)
in den Umfang von aufgelöstA
und auf angewendetcls
. Daalternate_constructor()
es sich um eine Klassenmethode handelt, übergibt der Aufrufsuper(B, cls).alternate_constructor(...)
implizit eine Referenz voncls
als erstes Argument anA
die Version vonalternate_constructor()
4-
super()
Um dasselbe zu tun, ohne es zu verwenden , müssten Sie einen Verweis auf die ungebundene Version vonA.alternate_constructor()
(dh die explizite Version der Funktion) erhalten. Einfach das zu tun würde nicht funktionieren:Dies würde nicht funktionieren, da die
A.alternate_constructor()
MethodeA
als erstes Argument einen impliziten Verweis auf verwendet . Das hier übergebenecls
Wesen wäre das zweite Argument.quelle
Viele gute Antworten, aber für visuelle Lernende: Lassen Sie uns zuerst mit Argumenten zu Super und dann ohne erkunden.
Stellen Sie sich eine Instanz vor,
jack
die aus der Klasse erstelltJack
wurde und deren Vererbungskette wie im Bild grün dargestellt ist. Berufung:super(Jack, jack).method(...)
verwendet die MRO (Method Resolution Order) von
jack
(seinen Vererbungsbaum in einer bestimmten Reihenfolge) und beginnt mit der Suche abJack
. Warum kann man eine Elternklasse anbieten? Wenn wir von der Instanzjack
aus suchen, wird die Instanzmethode gefunden. Der springende Punkt ist, die übergeordnete Methode zu finden.Wenn man super keine Argumente liefert, ist es wie das erste übergebene Argument die Klasse von
self
und das zweite übergebene Argument istself
. Diese werden in Python3 automatisch für Sie berechnet.Angenommen, wir möchten die
Jack
Methode nicht verwenden, anstatt sie weiterzugebenJack
, könnten wir sie übergebenJen
, um nach oben nach der Methode zu suchenJen
.Es durchsucht eine Schicht zu einer Zeit (Breite nicht Tiefe), zB wenn
Adam
undSue
beide haben die erforderlichen Verfahren, das eine vomSue
zuerst gefunden werden.Wenn
Cain
undSue
beide die erforderliche Methode hätten,Cain
würde die Methode zuerst aufgerufen. Dies entspricht im Code:MRO ist von links nach rechts.
quelle
Einige gute Antworten hier, aber sie befassen sich nicht mit der Verwendung
super()
in dem Fall, in dem verschiedene Klassen in der Hierarchie unterschiedliche Signaturen haben ... insbesondere im Fall von__init__
Um diesen Teil zu beantworten und ihn effektiv nutzen zu können,
super()
würde ich vorschlagen, meine Antwort super () zu lesen und die Signatur kooperativer Methoden zu ändern .Hier ist nur die Lösung für dieses Szenario:
Anwendungsbeispiel:
Ausgabe:
quelle
Das ist ziemlich leicht zu verstehen.
Ok, was passiert jetzt, wenn Sie verwenden
super(Child,self)
?Wenn eine untergeordnete Instanz erstellt wird, liegt ihre MRO (Method Resolution Order) in der Reihenfolge (Child, SomeBaseClass, object) basierend auf der Vererbung. (Angenommen, SomeBaseClass hat außer dem Standardobjekt keine anderen Eltern.)
Durch Übergeben
Child, self
wirdsuper
im MRO derself
Instanz gesucht und das Proxy-Objekt neben Child zurückgegeben. In diesem Fall handelt es sich um SomeBaseClass. Dieses Objekt ruft dann die__init__
Methode von SomeBaseClass auf. Mit anderen Worten, wenn dies der Fall istsuper(SomeBaseClass,self)
, ist das zurückgegebene Proxy-Objektsuper
object
Bei der Mehrfachvererbung kann die MRO viele Klassen enthalten, sodass Sie im Grunde
super
entscheiden können, wo Sie mit der Suche in der MRO beginnen möchten.quelle
Betrachten Sie den folgenden Code:
Wenn Sie den Konstruktor von
Y
to ändernX.__init__
, erhalten Sie:Aber
super(Y, self).__init__()
wenn Sie verwenden , erhalten Sie:Und
P
oderQ
kann sogar aus einer anderen Datei beteiligt sein, die Sie beim Schreiben nicht kennenX
undY
. Im Grunde wissen Sie also nicht, woraufsuper(Child, self)
Sie sich beim Schreiben beziehenclass Y(X)
, selbst die Signatur von Y ist so einfach wieY(X)
. Deshalb könnte Super eine bessere Wahl sein.quelle