Wie funktioniert Python ' s super() Arbeit mit Mehrfachvererbung?
Ich bin ziemlich neu in Python objektorientiert Programmieren und ich habe Mühe,
das Verständnis der super()
Funktion (new-style-Klassen), insbesondere wenn es um Mehrfachvererbung.
Wenn Sie zum Beispiel etwas 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 verstehe ist: wird die Third()
Klasse Erben, die beiden Konstruktor-Methoden? Wenn ja, dann welches ausgeführt wird mit super() und warum?
Und was ist, wenn Sie ausführen möchten, die anderen? Ich weiß, es hat etwas zu tun mit Python-Methode Auflösung um (MRO).
In der Tat, die mehrfache Vererbung ist der einzige Fall, wo
ist technisch korrekt, es ist ein kleiner Aufwand, aber super() ist mehr pythonic und ermöglicht re-factoring und änderungen an der code im Laufe der Zeit. Die Verwendung von super (), es sei denn, Sie wirklich brauchen, eine benannte Klasse Methode.
Ein weiteres problem mit
super()
ist. Ich würde nicht empfehlen es mit Klassen mit linearen Vererbung, wo es nur nutzlosen overhead.ist technisch korrekt, es ist ein kleiner Aufwand, aber super() ist mehr pythonic und ermöglicht re-factoring und änderungen an der code im Laufe der Zeit. Die Verwendung von super (), es sei denn, Sie wirklich brauchen, eine benannte Klasse Methode.
Ein weiteres problem mit
super()
ist, dass es zwingt jeder Unterklasse, es auch zu benutzen, während, wenn Sie nicht mit super()
jeder Unterklassen kann es selbst entscheiden. Wenn ein Entwickler es nicht kennen super()
oder nicht weiß, es wurde verwendet, Probleme mit der Instandhaltung auftreten können, sind sehr schwer aufzuspüren.
InformationsquelleAutor Callisto | 2010-07-18
Du musst angemeldet sein, um einen Kommentar abzugeben.
Dies ist detaillierten mit einen angemessenen Betrag von detail von Guido, der sich in seinem blog-post Methode Auflösung Um (einschließlich zwei frühere versuche).
In deinem Beispiel
Third()
rufenFirst.__init__
. Python sieht für jedes Attribut in der Klasse, die Eltern wie Sie aufgelistet sind, von Links nach rechts. In diesem Fall suchen wir für__init__
. Also, wenn Sie definierenPython wird beginnen, suchen Sie in
First
, und, wennFirst
nicht das Attribut, dann wird es schauenSecond
.Diese situation wird komplexer, wenn die Vererbung beginnt Kreuzung Pfade (zum Beispiel, wenn
First
geerbt vonSecond
). Lesen Sie den link oben für weitere details, aber in einer nussschale, Python werden versuchen, pflegen Sie die Reihenfolge, in der jede Klasse wird auf die Vererbung Liste, beginnend mit der Kind-Klasse selbst.So, zum Beispiel, wenn Sie hatte:
MRO wäre
[Fourth, Second, Third, First].
Übrigens: wenn Python nicht finden können, eine kohärente Methode Auflösung um, werde es zu einer Ausnahme, statt zu fallen zurück auf das Verhalten, die vielleicht überraschen die Nutzer.
Bearbeitet, um hinzufügen ein Beispiel für eine mehrdeutige MRO:
Sollte
Third
's MRO werden[First, Second]
oder[Second, First]
? Es gibt keine eindeutige Erwartung, und Python wird ein Fehler ausgelöst:Edit: ich sehe einige Leute argumentieren, dass die obigen Beispiele fehlen
super()
ruft, so lassen Sie mich erklären: Der Sinn der Beispiele ist zu zeigen, wie der MRO-konstruiert wird. Sie sind nicht soll print "first\nsecond\Dritte" oder was auch immer. Sie können – und sollten, natürlich, spielen, um mit dem Beispiel, fügen Siesuper()
Anrufe, sehen, was passiert, und ein tieferes Verständnis von Python-Vererbung-Modell. Aber mein Ziel ist es einfach zu halten und zu zeigen, wie der MRO-gebaut wird. Und es wird gebaut wie ich schon erklärt habe:Ich denke, das fehlen von super-Aufrufe in den ersten Klassen ist ein wirklich großes problem, mit dieser Antwort, ohne zu diskutieren, wie/warum das ist wichtig, kritisches Verständnis der Frage ist verloren.
Diese Antwort ist einfach falsch. Ohne super () - Aufrufe in der Eltern, wird nichts passieren. @leblos s Antwort ist die richtige.
Der Punkt dieses Beispiels ist, zu zeigen, wie der MRO-konstruiert wird. Das Beispiel ist NICHT vorgesehen, print "first\nsecond\Dritte" oder was auch immer. Und die Instandhaltung ist in der Tat richtig: der Vierte.__mro__ == (<die Klasse 'main.Vierte'>, <class 'main.Zweite'>, <class 'main.Dritte " >, <class 'main.Erste " >, <geben Sie 'Objekt'>)
Recht, wenn er sagt, es wird immer verwirrender. Ich folgte seiner Logik zu versuchen understading Instandhaltung und super in die Tiefe. Hier ist das, was ich gelernt habe. Jetzt ist es mir klar, daß in diesem Fall die Zweite.super rufen Dritte, obwohl Dritte ist nicht geerbt von der Zweiten.
InformationsquelleAutor rbp
Ihren code, und die anderen Antworten, die sind alle buggy. Sie fehlen der
super()
Anrufe in den ersten beiden Klassen, die erforderlich sind für die co-operative Unterklassen zu arbeiten.Hier ist eine korrigierte version des Codes:
Den
super()
Anruf findet die nächste Methode in der MRO-bei jedem Schritt, weshalb die Erste und Zweite haben es auch, ansonsten die Ausführung Stoppt am EndeSecond.__init__()
.Dies ist, was ich bekomme:
"co-operative Unterklassen"
Auf diese Weise wird der init Methoden BEIDER Basisklassen wird ausgeführt, während das ursprüngliche Beispiel wird nur der erste init begegnet in der Instandhaltung. Ich denke, das ist implizit durch den Begriff "co-operative Unterklassen", aber eine Klärung wäre es sinnvoll gewesen, ('Explizit ist besser als implizit", wissen Sie 😉 )
Ja, wenn Sie die übergabe verschiedener Parameter an eine Methode aufgerufen wird, über super, alle Implementierungen der Methode die MRO in Richtung Objekt() müssen mit kompatiblen Signaturen. Dies kann erreicht werden durch keyword-Parameter: akzeptieren mehr Parameter als die Methode verwendet, und ignorieren Sie zusätzliche. Seine in der Regel als hässlich, um dies zu tun, und für die meisten Fälle das hinzufügen von neuen Methoden besser ist, aber init ist (fast?) einzigartig wie eine spezielle Methode, Namen, aber mit dem benutzerdefinierten Parameter.
Das design der mehrere Vererbung ist wirklich wirklich schlecht in python. Die Basisklassen fast müssen wissen, wer wird zu leiten, und wie viele weitere Basis-Klassen, die abgeleitet wird, abgeleitet sind, und in welcher Reihenfolge... sonst
super
wird entweder nicht ausgeführt werden (weil der parameter-mismatch), oder es wird nicht rufen Sie einige der Basen (weil Sie nicht schreibensuper
in einer von der Basis bricht den link)!InformationsquelleAutor lifeless
Ich wollte aufwändige die Antwort von leblos ein bisschen, weil wenn ich anfing zu Lesen über die Verwendung von super() in einer mehrfach-Hierarchie-Vererbung in Python, habe ich nicht bekommen es sofort.
Was Sie verstehen müssen ist, dass
super(MyClass, self).__init__()
bietet die nächsten__init__
Methode, nach der Methode Auflösung Bestellen (MRO) - Algorithmus im Kontext der kompletten Vererbungshierarchie.Dieser Letzte Teil ist entscheidend, um zu verstehen. Wir betrachten nochmals das Beispiel:
Laut diesem Artikel über die Methode Auflösung Um , die von Guido van Rossum, der, um zu beheben
__init__
berechnet (vor Python 2.3) mit einer "Tiefe-zuerst, Links-nach-rechts-traversal" :Nach dem entfernen aller Duplikate, mit Ausnahme der letzten, die wir bekommen :
So, können verfolgen, was passiert, wenn wir instanziieren eine Instanz der
Third
Klasse, z.B.x = Third()
.Gemäß MRO
__init__
Dritter zum ersten mal aufgerufen wird.Nächsten, gemäß MRO innen der
__init__
Methodesuper(Third,
löst dieself).__init__()
__init__
Methode der Ersten, diewird aufgerufen.
Innen
__init__
des Erstensuper(First, self).__init__()
ruft die__init__
von Zweiten, weil das ist, was der MRO-Diktat!Innen
__init__
des Zweitensuper(Second, self).__init__()
Anrufedie
__init__
des Objekts, die Beträge zu nichts. Danach"zweite" ist gedruckt.
Nach
super(First, self).__init__()
abgeschlossen,"zuerst" wird gedruckt.
Nach
super(Third, self).__init__()
abgeschlossen, ", dass es" gedruckt.Diese details heraus, warum die Instanziierung Dritten() führt zu :
MRO-Algorithmus wurde verbessert von Python 2.3 weiter zu arbeiten, auch in komplexen Fällen, aber ich denke, das mit der "Tiefe-zuerst, Links-nach-rechts-traversal" + "entfernen der Duplikate erwarten für das Letzte" noch funktioniert in den meisten Fällen (bitte kommentieren, wenn dies nicht der Fall ist). Lesen Sie den blog-post von Guido!
Das erzeugte Objekt vom Typ der Dritten, die alle init-Methoden. Wenn Sie also davon ausgehen, MRO erstellt eine Liste alle init-Funktionen in einer bestimmten Reihenfolge, mit jeder super-Aufruf, Sie gehen einen Schritt vorwärts, bis Sie das Ende erreichen.
Ich denke, Schritt 3 braucht mehr Erklärung: Wenn
Third
nicht Erben ausSecond
, dannsuper(First, self).__init__
nennen würdeobject.__init__
und nach der Rückkehr, "first" ausgegeben würden. Aber daThird
erbt von beidenFirst
undSecond
eher als Berufungobject.__init__
nachFirst.__init__
MRO schreibt vor, dass nur der Letzte Aufrufobject.__init__
wird beibehalten, und die print-Anweisungen inFirst
undSecond
sind nicht erreicht, bisobject.__init__
gibt. DaSecond
war der Letzte Anrufobject.__init__
ist, gibt es innerhalbSecond
vor der Rückkehr inFirst
.Sie nagelte ihn, sehr gute Erklärung! Kodos!
Interessanterweise PyCharm scheint zu wissen, alle diese (seine Tipps sprechen Sie über die Parameter, mit denen Anrufe an super. Es hat auch einige Begriff der Kovarianz der Eingänge, so dass es erkennt
List[subclass]
alsList[superclass]
wennsubclass
ist eine Unterklasse vonsuperclass
(List
kommt aus dertyping
Modul PEP 483 iirc).InformationsquelleAutor Visionscaper
Dies ist bekannt als die Diamond-Problem, hat die Seite einen Eintrag in Python, sondern in kurzen, Python aufrufen, die Oberklasse die Methoden von Links nach rechts.
object
ist die vierteInformationsquelleAutor monoceres
Dies ist, wie ich die lösen in der Frage der Notwendigkeit von Mehrfachvererbung mit verschiedenen Variablen für die Initialisierung und mehrere Mixins in Verbindung mit dem gleichen Funktionsaufruf. Ich hatte explizit hinzufügen von Variablen zu übergeben, **kwargs und fügen Sie ein MixIn-interface ein Endpunkt für die super Gespräche.
Hier
A
ist eine erweiterbare Basis-Klasse undB
undC
sind MixIn-Klassen sowohl die Funktionf
.A
undB
beide erwarten parameterv
in Ihrer__init__
undC
erwartetw
.Die Funktion
f
nimmt einen parametery
.Q
erbt von allen drei Klassen.MixInF
wird das mixin-interface fürB
undC
.Theoretisch, ja. Praktisch, dieses Szenario hat jede Zeit, die ich habe festgestellt Diamant-Vererbung in python, also habe ich es hier. Da dies ist, wo ich gehen jedes mal, wenn ich nicht sauber vermeiden diamond-Vererbung. Hier sind einige zusätzliche links für die Zukunft mich: rhettinger.wordpress.com/2011/05/26/super-considered-super code.activestate.com/recipes/...
Was wir wollen, ist, dass Programme mit semantisch sinnvollen parameter-Namen. Aber in diesem Beispiel fast alle Parameter sind anonym benannt, die machen es viel schwieriger für die ursprünglichen Programmierer dokumentieren Sie den code und ein anderer Programmierer, den code zu Lesen.
Ein pull-request auf der github repo mit beschreibenden Namen wäre geschätzt
Ich denke, @Arthur bedeutete, dass Ihre ganze Ansatz beruht auf der Verwendung von
args
/kwargs
eher als benannte Parameter.InformationsquelleAutor brent.payne
Verstehe ich das nicht direkt beantworten die
super()
Frage, aber ich halte es für relevant genug, zu teilen.Da ist auch ein Weg, um den direkten Aufruf von jeder geerbten Klasse:
Nur beachten, dass, wenn Sie tun es auf diese Weise, müssen Sie zu rufen jedes manuell, da bin ich mir ziemlich sicher, dass
First
's__init__()
werden nicht genannt.First
undSecond
sind beide Erben von einer anderen Klasse aufrufen und direkt dann das Allgemeine Klasse (Ausgangspunkt der Diamant) zweimal aufgerufen. super ist die Vermeidung dieser.Ja, ich war zuversichtlich, es würde nicht. Allerdings habe ich nicht endgültig wissen, und ich wollte nicht zu sagen, wie wenn ich es Tat, auch wenn es sehr unwahrscheinlich ist. Das ist ein guter Punkt, um die
object
wird zweimal genannt. Ich glaube nicht, dass. Ich wollte nur darauf hinweisen, dass Sie anrufen übergeordneten Klassen direkt.Leider, das bricht, wenn init versucht, sich Zugriff auf private-Methoden 🙁
InformationsquelleAutor Seaux
Insgesamt
Vorausgesetzt, alles stammt aus
object
(Sie sind auf Ihre eigenen, wenn es nicht), Python berechnet eine Methode die Auflösung von Ordnung (MRO), basierend auf Ihrer Klasse vererbungsbaums. Der MRO-erfüllt 3 Eigenschaften:Wenn keine solche Reihenfolge gibt es, Python-Fehler aufgetreten. Das Innenleben ist dies eine C3-Linearisierung der Klassen Abstammung. Lesen Sie alles über es hier: https://www.python.org/download/releases/2.3/mro/
So, in beiden der folgenden Beispiele ist es:
Wenn eine Methode aufgerufen wird, wird das erste vorkommen dieser Methode in der Instandhaltung ist die, die aufgerufen wird. Jede Klasse, die nicht realisieren, dass die Methode übersprungen. Jeder Aufruf
super
innerhalb der Methode ruft die nächste Instanz, das Verfahren in der Instandhaltung. Folglich, es ist wichtig zu haben, was um Sie Platz Klassen in der Vererbung, und wo Sie die Anrufe zusuper
in den Methoden.Mit
super
ersten in jeder MethodeChild()
Ausgänge:Mit
super
letzten in jeder MethodeChild()
Ausgänge:Left
mitsuper()
ausChild
. angenommen, ich möchte den ZugangRight
von innenChild
. Gibt es eine Möglichkeit, den ZugangRight
ausChild
mit super? Oder sollte ich direkt genanntRight
von innensuper
?Wenn Sie möchten, um Zugriff auf die Methode einer bestimmten Klasse nur, sollten Sie auf die Klasse direkt, anstatt mit super. Super ist, etwa nach der Kette der Vererbung, die nicht immer zu einer bestimmten Klasse-Methode.
Vielen Dank für die ausdrückliche Erwähnung 'Einer Klasse nur einmal in der Instandhaltung'. Das ist mein problem gelöst. Jetzt endlich verstehe ich, wie mehrfache Vererbung funktioniert. Jemand benötigt zu schweigen von den Eigenschaften der MRO!
InformationsquelleAutor Zags
Über @calfzhou Kommentar, die Sie verwenden können, wie in der Regel,
**kwargs
:Online-Beispiel
Ergebnis:
Können Sie sich auch positionsbezogen:
aber Sie haben zu erinnern, die Instandhaltung, es ist wirklich verwirrend.
Kann ich ein wenig ärgerlich, aber ich bemerkte, dass die Menschen vergessen, jedes mal zu verwenden
*args
und**kwargs
wenn Sie eine Methode überschreiben, während es ein paar wirklich nützliche und vernünftige Verwendung dieser "magischen Variablen".InformationsquelleAutor Marco Sulla
Weiteren noch nicht erfassten Punkt ist die übergabe von Parametern für die Initialisierung von Klassen. Da das Ziel der
super
hängt von der Unterklasse die einzige gute Möglichkeit für die übergabe von Parametern packt Sie alle zusammen. Dann werden Sie vorsichtig, um nicht die gleichen parameter name mit verschiedenen Bedeutungen.Beispiel:
gibt:
Aufruf der super-Klasse
__init__
direkt für mehr direkte Zuordnung von Parametern ist verlockend, aber schlägt fehl, wenn es irgendeinesuper
Aufruf in einer super-Klasse und/oder die Instandhaltung wird geändert und die Klasse A kann mehrfach aufgerufen werden, abhängig von der Implementierung.Schließen: kooperative Vererbung und super und spezifische Parameter für die Initialisierung arbeiten nicht sehr gut zusammen.
InformationsquelleAutor Trilarion
Ausgabe
Aufruf von Dritten() sucht die init definiert in der Dritten. Und der Aufruf von super in diese routine ruft init definiert im Ersten. MRO=[Ersten, Zweiten].
Rufen Sie jetzt super in init definiert im Ersten weiterhin auf der Suche Instandhaltung und finden init definiert, in der Zweiten, und jeder Aufruf von super treffen wird das Standard-Objekt init. Ich hoffe dieses Beispiel verdeutlicht das Konzept.
Wenn Sie nicht rufen Sie super von der Ersten. Die Kette beendet, und Sie erhalten die folgende Ausgabe.
das war, veranschaulichen die aufrufreihenfolge
InformationsquelleAutor Seraj Ahmad
Ich hinzufügen möchte, was @Visionscaper sagt an der Spitze:
In diesem Fall wird der interpreter nicht herausfiltern, die das Objekt der Klasse, weil seine dupliziert, sondern seine weil der Zweite erscheint in einer Kopf-position und nicht erscheint in den Schwanz position in einer Hierarchie Teilmenge. Während Objekt erscheint nur in der Schwanz-Positionen und ist nicht als eine starke position in C3-Algorithmus, um zu bestimmen, Priorität.
Die Linearisierung(mro) von der Klasse C L(C), ist die
Linearisierte Zusammenführen wird durch Auswahl des gemeinsamen Klassen, der aussieht wie der Kopf von Listen-und nicht der Schwanz da, um Fragen(wird klar, unten)
Die Linearisierung von Dritten berechnet werden kann wie folgt:
Somit für einen super () - Implementierung in dem folgenden code:
wird deutlich, wie diese Methode gelöst werden
Schwanz position bezieht sich auf Klassen, die höher in der Hierarchie der Klasse und Umgekehrt. Die Basis-Klasse "object" wird an das Ende des Schwanzes. Der Schlüssel zum Verständnis der mro-Algorithmus ist, wie die 'Zweite' erscheint als super-der 'Erste'. Würden wir normalerweise davon ausgehen, dass es die "Objekt" - Klasse. Das ist wahr, aber nur in der Perspektive der "Ersten" Klasse. Aber wenn man von der "Dritten" Klasse Perspektive, die Hierarchie, um für die 'Erste' ist anders und wird berechnet wie oben dargestellt. mro-Algorithmus versucht, diese Perspektive(oder Hierarchie Teilmenge) für mehrere geerbte Klassen
InformationsquelleAutor supi
In learningpythonthehardway Lerne ich etwas namens super() eine integrierte Funktion, wenn nicht Irre. Aufruf von super () - Funktion kann helfen, die Vererbung durch die Eltern und 'Geschwister' und helfen Ihnen, klarer zu sehen. Ich bin immer noch ein Anfänger, aber ich Liebe es, meine Erfahrungen über die Verwendung von super() in python2.7.
Wenn Sie haben Lesen Sie die Kommentare auf dieser Seite, Sie hören von Method Resolution Order (MRO), die Methode, die die Funktion, die Sie schrieb, MRO wird mit Tiefe-Zuerst, Links-Rechts-Schema zu suchen und zu führen. Sie können mehr tun, Forschung.
Durch Zugabe von super () - Funktion
Verbinden, können Sie mehrere Instanzen und 'Familien', die mit super(), indem in jedem und jeder in Ihnen. Und es werden die Methoden ausführen, gehen Sie Sie durch, und stellen Sie sicher, dass Sie nicht verpassen! Jedoch, indem Sie vor oder nach macht einen Unterschied, werden Sie wissen, wenn Sie getan haben, die learningpythonthehardway Aufgabe 44. Der Spaß kann beginnen!!
Unter Beispiel unten können Sie kopieren & einfügen und versuchen, führen Sie es:
Wie funktioniert es ausführen? Die Instanz der fünften() geht so. Jeder Schritt geht von Klasse zu Klasse, wo die super-Funktion Hinzugefügt.
Den Eltern gefunden wurde und es geht weiter zur Dritten und Vierten!!
Nun alle Klassen mit super() zugegriffen wurde! Die Eltern der Klasse gefunden und ausgeführt und jetzt geht es weiter zu unbox die Funktion der Erbschaften auf die fertigen codes.
Das Ergebnis der Anwendung oben:
Für mich durch Zugabe von super() ermöglicht es mir, klarer zu sehen, wie python ausgeführt würde meine Codierung, und stellen Sie sicher, dass die erbschaft zugreifen können, die Methode, die ich soll.
InformationsquelleAutor Will MeetYou
Vielleicht gibt es noch etwas, das Hinzugefügt werden kann, ein kleines Beispiel mit Django rest_framework und Dekorateure. Dies bietet eine Antwort auf die implizite Frage: "warum möchte ich das überhaupt?"
Wie gesagt: wir sind mit Django rest_framework, und wir sind mit Hilfe von generischen Ansichten, und für jede Art von Objekten in unserer Datenbank finden wir uns mit einer view-Klasse, die Bereitstellung von GET-und POST für Listen von Objekten, und eine andere Ansicht-Klasse bietet GET, PUT und DELETE für einzelne Objekte.
Nun die POST, PUT und DELETE wir wollen zu dekorieren mit Django ' s login_required. Beachten Sie, wie diese berührt sowohl Klassen, aber nicht alle Methoden in beiden Klassen.
Könnte eine Lösung gehen, die durch Mehrfachvererbung.
Ebenso für die anderen Methoden.
In der Vererbung Liste meiner konkreten Klassen, ich möchte hinzufügen, meine
LoginToPost
vorListCreateAPIView
undLoginToPutOrDelete
vorRetrieveUpdateDestroyAPIView
. Meine konkrete Klassen'get
bleiben würde, schmucklos.InformationsquelleAutor mariotomo