Ich bin ziemlich neu in der objektorientierten Programmierung von Python und habe Probleme, die super()
Funktion (neue Stilklassen) zu verstehen, insbesondere wenn es um Mehrfachvererbung geht.
Zum Beispiel, wenn Sie etwas haben wie:
class First(object):
def __init__(self):
print "first"
class Second(object):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"
Was ich nicht bekomme ist: wird das Third()
Klasse beide Konstruktormethoden erben? Wenn ja, welches wird dann mit super () ausgeführt und warum?
Und was ist, wenn Sie den anderen ausführen möchten? Ich weiß, dass es etwas mit der Python Method Resolution Resolution ( MRO ) zu tun hat .
python
multiple-inheritance
Callisto
quelle
quelle
super()
von Nutzen ist. Ich würde nicht empfehlen, es mit Klassen zu verwenden, die lineare Vererbung verwenden, wo es nur nutzlosen Overhead ist.super()
besteht darin, dass jede Unterklasse gezwungen wird, sie ebenfalls zu verwenden. Wenn sie nicht verwendet wirdsuper()
, kann jeder, der sie verwendet, selbst entscheiden. Wenn ein Entwickler, der es verwendet, nichts darüber weißsuper()
oder nicht weiß, dass es verwendet wurde, können Probleme mit dem mro auftreten, die sehr schwer zu finden sind.Antworten:
Dies wird von Guido selbst in seinem Blog-Beitrag Method Resolution Order (einschließlich zweier früherer Versuche) mit einer angemessenen Menge an Details beschrieben .
In Ihrem Beispiel
Third()
wird anrufenFirst.__init__
. Python sucht nach jedem Attribut in den Eltern der Klasse, da diese von links nach rechts aufgelistet sind. In diesem Fall suchen wir__init__
. Also, wenn Sie definierenPython beginnt mit dem Betrachten
First
und wennFirst
es das Attribut nicht hat, wird es sich ansehenSecond
.Diese Situation wird komplexer, wenn die Vererbung beginnt, Pfade zu kreuzen (z. B. wenn sie
First
vererbt wirdSecond
). Lesen Sie den obigen Link, um weitere Informationen zu erhalten. Kurz gesagt, Python versucht, die Reihenfolge beizubehalten, in der jede Klasse in der Vererbungsliste angezeigt wird, beginnend mit der untergeordneten Klasse selbst.Also zum Beispiel, wenn Sie hatten:
der MRO wäre
[Fourth, Second, Third, First].
Übrigens: Wenn Python keine kohärente Reihenfolge für die Methodenauflösung findet, wird eine Ausnahme ausgelöst, anstatt auf ein Verhalten zurückzugreifen, das den Benutzer überraschen könnte.
Bearbeitet, um ein Beispiel für eine mehrdeutige MRO hinzuzufügen:
Sollte
Third
MRO sein[First, Second]
oder[Second, First]
? Es gibt keine offensichtliche Erwartung und Python wird einen Fehler auslösen:Bearbeiten: Ich sehe mehrere Leute argumentieren, dass die obigen Beispiele keine
super()
Aufrufe enthalten. Lassen Sie mich daher erklären: In den Beispielen soll gezeigt werden, wie die MRO aufgebaut ist. Sie sollen nicht "first \ nsecond \ Third" oder was auch immer drucken. Sie können - und sollten natürlich mit dem Beispiel herumspielen,super()
Aufrufe hinzufügen , sehen, was passiert, und ein tieferes Verständnis des Vererbungsmodells von Python erlangen. Aber mein Ziel hier ist es, es einfach zu halten und zu zeigen, wie der MRO aufgebaut ist. Und es ist so gebaut, wie ich es erklärt habe:quelle
Ihr Code und die anderen Antworten sind alle fehlerhaft. Ihnen fehlen die
super()
Aufrufe in den ersten beiden Klassen, die erforderlich sind, damit die kooperative Unterklasse funktioniert.Hier ist eine feste Version des Codes:
Der
super()
Aufruf findet bei jedem Schritt die nächste Methode im MRO, weshalb First und Second sie ebenfalls haben müssen, andernfalls wird die Ausführung am Ende von beendetSecond.__init__()
.Das bekomme ich:
quelle
super
wird sie entweder nicht ausgeführt (aufgrund einer Nichtübereinstimmung der Parameter) oder sie werden nicht aufgerufen einige der Basen (weil Sie nichtsuper
in eine der Basen geschrieben haben, die den Link unterbricht)!Ich wollte die Antwort ein wenig leblos ausarbeiten, denn als ich anfing zu lesen, wie man super () in einer Hierarchie mit mehreren Vererbungen in Python verwendet, bekam ich sie nicht sofort.
Was Sie verstehen müssen, ist, dass
super(MyClass, self).__init__()
die nächste__init__
Methode gemäß dem verwendeten MRO-Algorithmus (Method Resolution Ordering) im Kontext der vollständigen Vererbungshierarchie bereitgestellt wird .Dieser letzte Teil ist wichtig zu verstehen. Betrachten wir das Beispiel noch einmal:
Gemäß diesem Artikel über die Methodenauflösungsreihenfolge von Guido van Rossum wird die Auflösungsreihenfolge
__init__
(vor Python 2.3) unter Verwendung einer "Tiefenüberquerung von links nach rechts" berechnet:Nachdem wir alle Duplikate außer dem letzten entfernt haben, erhalten wir:
Folgen wir also dem, was passiert, wenn wir eine Instanz der
Third
Klasse instanziieren , zx = Third()
.Third.__init__
ausgeführt.Third(): entering
super(Third, self).__init__()
ausgeführt und MRO kehrt zurück,First.__init__
was aufgerufen wird.First.__init__
wird ausgeführt.First(): entering
super(First, self).__init__()
ausgeführt und MRO kehrt zurück,Second.__init__
was aufgerufen wird.Second.__init__
wird ausgeführt.Second(): entering
super(Second, self).__init__()
ausgeführt und MRO kehrt zurück,object.__init__
was aufgerufen wird.object.__init__
wird ausgeführt (keine Druckanweisungen im Code dort)Second.__init__
und druckt dannSecond(): exiting
First.__init__
und druckt dannFirst(): exiting
Third.__init__
und druckt dannThird(): exiting
Hier erfahren Sie, warum das Instanziieren von Third () zu Folgendem führt:
Der MRO-Algorithmus wurde ab Python 2.3 verbessert, um in komplexen Fällen gut zu funktionieren, aber ich denke, dass die Verwendung des "Durchlaufs von links nach rechts" + "Entfernen von Duplikaten, die für den letzten erwartet werden" in den meisten Fällen immer noch funktioniert (bitte Kommentar, wenn dies nicht der Fall ist). Lesen Sie unbedingt den Blog-Beitrag von Guido!
quelle
Third
nicht von geerbtSecond
, dannsuper(First, self).__init__
würde aufrufenobject.__init__
und nach der Rückkehr würde "zuerst" gedruckt. Aber weilThird
erbt von beidenFirst
undSecond
anstattobject.__init__
nachFirst.__init__
dem MRO anzurufen, diktiert, dass nur der letzte Aufruf vonobject.__init__
erhalten bleibt und die print-Anweisungen inFirst
undSecond
erst erreicht werden, wenn sieobject.__init__
zurückkehren. DaSecond
der letzte Anruf getätigt wurdeobject.__init__
, kehrt er nach innen zurück,Second
bevor er zurückkehrtFirst
.List[subclass]
alsList[superclass]
ifsubclass
eine Unterklasse von erkenntsuperclass
(List
stammt aus demtyping
Modul von PEP 483) iirc).Dies ist als Diamond-Problem bekannt . Die Seite enthält einen Eintrag zu Python. Kurz gesagt, Python ruft die Methoden der Oberklasse von links nach rechts auf.
quelle
object
ist der vierteAuf diese Weise habe ich das Problem gelöst, dass mehrere Vererbungen mit unterschiedlichen Variablen für die Initialisierung und mehrere MixIns mit demselben Funktionsaufruf vorhanden sind. Ich musste explizit Variablen zu übergebenen ** kwargs hinzufügen und eine MixIn-Schnittstelle hinzufügen, um ein Endpunkt für Superaufrufe zu sein.
Hier
A
ist eine erweiterbare Basisklasse undB
undC
sind MixIn-Klassen, die beide Funktionen bereitstellenf
.A
undB
beide erwarten Parameterv
in ihrem__init__
undC
erwartetw
. Die Funktionf
nimmt einen Parameter any
.Q
erbt von allen drei Klassen.MixInF
ist die Mixin-Schnittstelle fürB
undC
.quelle
args
/kwargs
und nicht auf benannten Parametern beruht .Ich verstehe, dass dies die
super()
Frage nicht direkt beantwortet , aber ich denke, dass es relevant genug ist, um es zu teilen.Es gibt auch eine Möglichkeit, jede geerbte Klasse direkt aufzurufen:
Es genügt zu bemerken , dass , wenn Sie es auf diese Weise tun, werden Sie jede manuell aufrufen müssen , wie ich bin ziemlich sicher ,
First
‚s__init__()
wird nicht aufgerufen werden.quelle
First
undSecond
beide eine andere Klasse zu erben und wird direkt dann diese gemeinsame Klasse (Ausgangspunkt des Diamanten) zweimal aufgerufen aufrufen. Super vermeidet dies.object
wenn man zweimal angerufen wird. Daran habe ich nicht gedacht. Ich wollte nur darauf hinweisen, dass Sie Elternklassen direkt aufrufen.Insgesamt
Angenommen, alles stammt von
object
(Sie sind allein, wenn dies nicht der Fall ist), berechnet Python eine Methodenauflösungsreihenfolge (MRO) basierend auf Ihrem Klassenvererbungsbaum. Der MRO erfüllt 3 Eigenschaften:Wenn keine solche Reihenfolge vorhanden ist, treten Python-Fehler auf. Das Innenleben ist eine C3-Linerisierung der Klassenvorfahren. Lesen Sie hier alles darüber: https://www.python.org/download/releases/2.3/mro/
In beiden folgenden Beispielen heißt es also:
Wenn eine Methode aufgerufen wird, wird diese Methode zum ersten Mal in der MRO aufgerufen. Jede Klasse, die diese Methode nicht implementiert, wird übersprungen. Jeder Aufruf
super
innerhalb dieser Methode ruft das nächste Auftreten dieser Methode in der MRO auf. Folglich ist es wichtig, in welcher Reihenfolge Sie Klassen in die Vererbung einfügen und wohin Sie die Aufrufe setzensuper
in den Methoden platzieren.Mit
super
zuerst in jeder MethodeChild()
Ausgänge:Mit
super
last in jeder MethodeChild()
Ausgänge:quelle
Left
mitsuper()
von zugreifen könnenChild
. Angenommen, ich möchteRight
von innen darauf zugreifenChild
. Gibt es eine Möglichkeit , den ZugangRight
vonChild
Super mit? Oder soll ich direktRight
von innen anrufensuper
?Über den Kommentar von @ calfzhou können Sie wie gewohnt Folgendes verwenden:
**kwargs
:Online laufendes Beispiel
Ergebnis:
Sie können sie auch positionell verwenden:
Aber Sie müssen sich an die MRO erinnern, es ist wirklich verwirrend.
Ich kann ein wenig nervig sein, aber ich habe bemerkt, dass die Leute jedes Mal vergessen haben, eine Methode zu verwenden
*args
und**kwargs
wenn sie sie überschreiben, während dies eine der wenigen wirklich nützlichen und vernünftigen Anwendungen dieser 'magischen Variablen' ist.quelle
Ein weiterer noch nicht behandelter Punkt ist die Übergabe von Parametern für die Initialisierung von Klassen. Da das Ziel von
super
von der Unterklasse abhängt, besteht die einzige gute Möglichkeit, Parameter zu übergeben, darin, sie alle zusammen zu packen. Achten Sie dann darauf, dass Sie nicht denselben Parameternamen mit unterschiedlichen Bedeutungen haben.Beispiel:
gibt:
Das
__init__
direkte Aufrufen der Superklasse zur direkteren Zuweisung von Parametern ist verlockend, schlägt jedoch fehl, wenn es welche gibtsuper
in einer Superklasse Aufruf erfolgt und / oder die MRO geändert wird und Klasse A je nach Implementierung mehrmals aufgerufen werden kann.Fazit: Kooperative Vererbung und Super- und spezifische Parameter für die Initialisierung arbeiten nicht sehr gut zusammen.
quelle
Ausgabe ist
Call to Third () sucht den in Third definierten Init . Und der Aufruf von super in dieser Routine ruft init auf, das in First definiert ist. MRO = [Erste, Zweite]. Jetzt ruft der Aufruf von super in init, definiert in First, die Suche nach MRO auf und findet init, definiert in Second, und jeder Aufruf von super trifft das Standardobjekt init . Ich hoffe, dieses Beispiel verdeutlicht das Konzept.
Wenn Sie nicht super von First anrufen. Die Kette stoppt und Sie erhalten die folgende Ausgabe.
quelle
Beim Lernen von Python auf dem Weg lerne ich etwas, das als super () bezeichnet wird, eine eingebaute Funktion, wenn nicht falsch. Das Aufrufen der Funktion super () kann dazu beitragen, dass die Vererbung die Eltern und Geschwister passiert und Sie klarer sehen. Ich bin noch ein Anfänger, aber ich liebe es, meine Erfahrungen mit der Verwendung dieses super () in Python2.7 zu teilen.
Wenn Sie die Kommentare auf dieser Seite gelesen haben, hören Sie von Method Resolution Order (MRO), wobei die Methode die von Ihnen geschriebene Funktion ist. MRO verwendet zum Suchen und Ausführen das Schema Tiefe zuerst von links nach rechts. Sie können mehr darüber recherchieren.
Durch Hinzufügen der Funktion super ()
Sie können mehrere Instanzen und 'Familien' mit super () verbinden, indem Sie jede einzelne Instanz hinzufügen. Und es wird die Methoden ausführen, sie durchgehen und sicherstellen, dass Sie nichts verpasst haben! Wenn Sie sie jedoch vorher oder nachher hinzufügen, werden Sie feststellen, ob Sie die Lernpythonthehardway-Übung 44 durchgeführt haben. Lassen Sie den Spaß beginnen!
Im folgenden Beispiel können Sie es kopieren, einfügen und ausführen:
Wie läuft es? Die Instanz von 5th () wird so aussehen. Jeder Schritt geht von Klasse zu Klasse, wo die Superfunktion hinzugefügt wird.
Der Elternteil wurde gefunden und es wird weiter bis zum dritten und vierten gehen !!
Jetzt wurde auf alle Klassen mit super () zugegriffen! Die übergeordnete Klasse wurde gefunden und ausgeführt und entpackt nun weiterhin die Funktion in den Vererbungen, um die Codes fertigzustellen.
Das Ergebnis des obigen Programms:
Durch Hinzufügen von super () kann ich klarer sehen, wie Python meine Codierung ausführen würde, und sicherstellen, dass die Vererbung auf die von mir beabsichtigte Methode zugreifen kann.
quelle
Ich möchte hinzufügen, was @Visionscaper oben sagt :
In diesem Fall filtert der Interpreter die Objektklasse nicht heraus, weil sie dupliziert wurde, sondern weil Second an einer Kopfposition und nicht an der Endposition in einer Hierarchie-Teilmenge erscheint. Das Objekt erscheint nur in Endpositionen und wird im C3-Algorithmus nicht als starke Position zur Bestimmung der Priorität angesehen.
Die Linearisierung (mro) einer Klasse C, L (C) ist die
Die linearisierte Zusammenführung erfolgt durch Auswahl der allgemeinen Klassen, die als Listenkopf und nicht als Endpunkt angezeigt werden, da die Reihenfolge wichtig ist (wird unten deutlich).
Die Linearisierung von Third kann wie folgt berechnet werden:
Also für eine super () Implementierung im folgenden Code:
Es wird deutlich, wie diese Methode gelöst wird
quelle
In Python 3.5+ sieht die Vererbung vorhersehbar und für mich sehr gut aus. Bitte schauen Sie sich diesen Code an:
Ausgänge:
Wie Sie sehen können, wird foo genau EIN Mal für jede geerbte Kette in derselben Reihenfolge aufgerufen, in der sie geerbt wurde. Sie können diese Bestellung erhalten, indem Sie anrufen . mro :
Viertens -> Dritte -> Erste -> Zweite -> Basis -> Objekt
quelle
Vielleicht kann noch etwas hinzugefügt werden, ein kleines Beispiel mit Django rest_framework und Dekorateuren. Dies gibt eine Antwort auf die implizite Frage: "Warum sollte ich das überhaupt wollen?"
Wie gesagt: Wir arbeiten mit Django rest_framework und verwenden generische Ansichten. Für jeden Objekttyp in unserer Datenbank gibt es eine Ansichtsklasse, die GET und POST für Objektlisten bereitstellt, und eine andere Ansichtsklasse, die GET bereitstellt , PUT und DELETE für einzelne Objekte.
Jetzt möchten wir POST, PUT und DELETE mit Djangos login_required dekorieren. Beachten Sie, wie dies beide Klassen berührt, jedoch nicht alle Methoden in beiden Klassen.
Eine Lösung kann mehrfach vererbt werden.
Ebenso für die anderen Methoden.
In der Vererbungsliste meiner konkreten Klassen würde ich meine
LoginToPost
VorherListCreateAPIView
undLoginToPutOrDelete
Vorher hinzufügenRetrieveUpdateDestroyAPIView
. Meine konkreten Klassenget
würden nicht dekoriert bleiben.quelle
Poste diese Antwort für meine zukünftige Referenz.
Python Multiple Inheritance sollte ein Diamantmodell verwenden und die Funktionssignatur sollte sich im Modell nicht ändern.
Das Beispielcode-Snippet wäre: -
Hier ist Klasse A.
object
quelle
A
sollte auch anrufen__init__
.A
hat die Methode nicht "erfunden"__init__
, daher kann nicht davon ausgegangen werden, dass eine andere KlasseA
früher in ihrer MRO war. Die einzige Klasse, deren__init__
Methode nicht aufruft (und nicht aufrufen sollte),super().__init__
istobject
.object
Vielleicht denke ich, ich sollteclass A (object) :
stattdessen schreibenA
kann nicht sein,object
wenn Sie einen Parameter zu seiner hinzufügen__init__
.