Was macht 'super' in Python?

564

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, superdass 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.

user25785
quelle

Antworten:

309

Die Vorteile der super()Einzelvererbung sind minimal - meistens müssen Sie den Namen der Basisklasse nicht in jeder Methode fest codieren, die ihre übergeordneten Methoden verwendet.

Es ist jedoch fast unmöglich, Mehrfachvererbung ohne zu verwenden super(). Dies schließt gängige Redewendungen wie Mixins, Interfaces, abstrakte Klassen usw. ein. Dies gilt auch für Code, der später Ihren erweitert. Wenn jemand später eine erweiterte Klasse Childund ein Mixin schreiben wollte, würde sein Code nicht richtig funktionieren.

John Millikin
quelle
6
Können Sie ein Beispiel dafür geben, was Sie mit "es würde nicht richtig funktionieren" meinen?
Charlie Parker
319

Was ist der Unterschied?

SomeBaseClass.__init__(self) 

Mittel zum Aufruf SomeBaseClass‚s __init__. während

super(Child, self).__init__()

bedeutet, eine Grenze __init__aus der übergeordneten Klasse aufzurufen , die Childin 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 superSie 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 supereine Sache, die Benutzern des Codes eine größere Flexibilität ermöglicht.

Python 2 gegen 3

Dies funktioniert in Python 2 und 3:

super(Child, self).__init__()

Dies funktioniert nur in Python 3:

super().__init__()

Es funktioniert ohne Argumente, indem es im Stapelrahmen nach oben geht und das erste Argument für die Methode abruft (normalerweise selffür eine Instanzmethode oder clsfü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 superSie 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 superkann es schwierig sein , die Argumente und die richtigen Methodenargumente richtig zu finden. Wenn Sie wissen, dass Sie die superEinzelvererbung 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:

class SomeBaseClass(object):
    def __init__(self):
        print('SomeBaseClass.__init__(self) called')

class UnsuperChild(SomeBaseClass):
    def __init__(self):
        print('UnsuperChild.__init__(self) called')
        SomeBaseClass.__init__(self)

class SuperChild(SomeBaseClass):
    def __init__(self):
        print('SuperChild.__init__(self) called')
        super(SuperChild, self).__init__()

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):

class InjectMe(SomeBaseClass):
    def __init__(self):
        print('InjectMe.__init__(self) called')
        super(InjectMe, self).__init__()

class UnsuperInjector(UnsuperChild, InjectMe): pass

class SuperInjector(SuperChild, InjectMe): pass

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:

>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called

Die Klasse mit dem verwendeten Kind superkann die Abhängigkeit jedoch korrekt einfügen:

>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called

Einen Kommentar adressieren

Warum in aller Welt wäre das nützlich?

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 sein

  1. Holen Sie sich die mro aus dem Typ der Instanz
  2. Suchen Sie nach dem Typ, der die Methode definiert
  3. Finden Sie den nächsten Typ mit der Methode
  4. Binden Sie diese Methode und rufen Sie sie mit den erwarteten Argumenten auf

Die UnsuperChildsollten keinen Zugang zu haben InjectMe. Warum lautet die Schlussfolgerung "Immer vermeiden super" nicht? Was fehlt mir hier?

Der UnsuperChildhat keinen Zugriff auf InjectMe. Es ist das UnsuperInjector, das Zugriff auf InjectMedie Methode dieser Klasse hat und diese dennoch nicht von der Methode aufrufen kann, von der es erbt UnsuperChild.

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 superHardcodierung 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 superauf 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, superkönnen die Benutzer Ihres Codes unnötige Einschränkungen erfahren.

Aaron Hall
quelle
In C ist DI so . Code ist hier . Wenn ich eine weitere Implementierung der listSchnittstelle hinzufüge, wird sie beispielsweise von doublylinkedlistder Anwendung reibungslos ausgewählt. Ich kann mein Beispiel konfigurierbarer machen, indem ich die config.txtImplementierung 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 Code
Überaustausch
Eine neue Implementierung wird beispielsweise durch Vererbung erstellt, wobei eine der "Injector" -Klassen von der InjectMeKlasse 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.
Aaron Hall
gute Antwort! Bei Verwendung der Mehrfachvererbung treten jedoch Komplikationen mit super () und __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 konzentriert
Aviad Rozenhek
35

Ich hatte ein bisschen mit gespielt super()und erkannt, dass wir die Anrufreihenfolge ändern können.

Zum Beispiel haben wir die nächste Hierarchiestruktur:

    A
   / \
  B   C
   \ /
    D

In diesem Fall ist MRO von D (nur für Python 3):

In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)

Erstellen wir eine Klasse, in super()der nach der Ausführung der Methode aufgerufen wird.

In [23]: class A(object): #  or with Python 3 can define class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          print("I'm from B")
...:          super().__init__()
...:   
...: class C(A):
...:      def __init__(self):
...:          print("I'm from C")
...:          super().__init__()
...:  
...: class D(B, C):
...:      def __init__(self):
...:          print("I'm from D")
...:          super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A

    A
   / 
  B  C
    /
    D

Wir können also sehen, dass die Auflösungsreihenfolge dieselbe ist wie in MRO. Aber wenn wir super()am Anfang der Methode aufrufen :

In [21]: class A(object):  # or class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          super().__init__()  # or super(B, self).__init_()
...:          print("I'm from B")
...:   
...: class C(A):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from C")
...:  
...: class D(B, C):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from D")
...: d = D()
...: 
I'm from A
I'm from C
I'm from B
I'm from D

Wir haben eine andere Reihenfolge, es ist eine umgekehrte Reihenfolge des MRO-Tupels.

    A
   / 
  B  C
    /
    D 

Für zusätzliche Lektüre würde ich die nächsten Antworten empfehlen:

  1. C3-Linearisierungsbeispiel mit Super (eine große Hierarchie)
  2. Wichtige Verhaltensänderungen zwischen alten und neuen Stilklassen
  3. Die Insider-Geschichte über Klassen neuen Stils
SKhalymon
quelle
Ich verstehe nicht, warum sich die Reihenfolge ändert. Der erste Teil Ich verstehe, dass DBCA, weil D die erste Klasse ist, dann, wenn das Selbst (B, C) geladen wird, schließlich B, C dann nur A druckt, da B (A), C (A) für das Finale auf sich selbst zurückweisen Teil. Wenn ich diesem Verständnis folge, sollte der zweite Teil dann nicht wie BCAD sein? Könnten Sie mir bitte etwas erklären?
JJson
Mein schlechtes, ich habe nicht bemerkt, dass jede Klasseninstanz zuerst mit super () initiiert wurde. Wenn dies der Fall ist, sollte es dann nicht ABCD sein? Ich verstehe irgendwie, wie ACBD dazu kam, konnte aber immer noch nicht überzeugen und habe immer noch ein bisschen Verwirrung. Mein Verständnis ist, dass d = D () die Klasse D (B, C) mit 2 Selbstparametern genannt wird, da super () zuerst initiiert wird, dann B zusammen mit seinen Attributen aufgerufen wird und dann D nicht gedruckt wird, bevor C weil Klasse ist D (B, C) enthält 2 Selbstparameter, daher muss der zweite ausgeführt werden, der Klasse C (A) ist. Nach der Ausführung müssen keine weiteren Selbstparameter ausgeführt werden
JJson
Es basiert auf der mro- Definition.
SKhalymon
1
dann wird C gedruckt, dann B und schließlich D. Habe ich recht?
JJson
2
Es ist sehr einfach, den zweiten zu verstehen, solange Sie den ersten bekommen. Es ist wie ein Stapel. Sie schieben den Druck '' in den Stapel und machen super (). Wenn das A fertig ist, werden die Dinge in diesem Stapel gedruckt, sodass die Reihenfolge umgekehrt ist.
gewähren Sonne
35

Geht das nicht alles davon aus, dass die Basisklasse eine Klasse neuen Stils ist?

class A:
    def __init__(self):
        print("A.__init__()")

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

Funktioniert nicht in Python 2. class Amuss neu sein, dh:class A(object)

mhawke
quelle
20

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, Bund Cwobei jede Klasse ist die Mutter des einen darauf folgenden, und a, bund cjeweilige Instanzen der einzelnen.

super(B, b) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to b, as if b was an instance of A

super(C, c) 
# resolves to the scope of C's parent i.e. B
# and applies that scope to c

super(B, c) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to c

Verwenden supermit einer statischen Methode

zB super()aus der __new__()Methode heraus verwenden

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return super(A, cls).__new__(cls, *a, **kw)

Erlä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:

# if you defined this
class A(object):
    def __new__(cls):
        pass

# calling this would raise a TypeError due to the missing argument
A.__new__()

# whereas this would be fine
A.__new__(A)

2- Wenn super()wir aufrufen , um zur übergeordneten Klasse zu gelangen, übergeben wir die untergeordnete Klasse Aals erstes Argument. Dann übergeben wir einen Verweis auf das interessierende Objekt. In diesem Fall ist es die Klassenreferenz, die beim A.__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.

super(A, cls)

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 insterest cls.

super(A, cls).__new__(cls, *a, **kw)

4- das Gleiche tun ohne super

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return object.__new__(cls, *a, **kw)

Verwenden supermit einer Instanzmethode

zB super()von innen benutzen__init__()

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        super(A, self).__init__(*a, **kw)

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:

# you try calling `__init__()` from the class without specifying an instance
# and a TypeError is raised due to the expected but missing reference
A.__init__() # TypeError ...

# you create an instance
a = A()

# you call `__init__()` from that instance and it works
a.__init__()

# you can also call `__init__()` with the class and explicitly pass the instance 
A.__init__(a)

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.

super(A, self)

3- Der Aufruf super(A, self)gibt einen Proxy zurück, der den Bereich auflöst und auf ihn anwendet, selfals wäre er jetzt eine Instanz der übergeordneten Klasse. Nennen wir diesen Proxy s. Da __init__()es sich um eine Instanzmethode handelt, übergibt der Aufruf s.__init__(...)implizit einen Verweis selfals erstes Argument an den übergeordneten __init__().

4- Um dasselbe zu tun, ohne dass superwir einen Verweis auf eine Instanz explizit an die übergeordnete Version von übergeben müssen __init__().

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        object.__init__(self, *a, **kw)

Verwenden supermit einer Klassenmethode

class A(object):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        print "A.alternate_constructor called"
        return cls(*a, **kw)

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return super(B, cls).alternate_constructor(*a, **kw)

Erläuterung:

1- Eine Klassenmethode kann direkt von der Klasse aufgerufen werden und verwendet als ersten Parameter einen Verweis auf die Klasse.

# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()

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.

super(B, cls_or_subcls)

3- Der Anruf wird super(B, cls)in den Umfang von aufgelöst Aund auf angewendet cls. Da alternate_constructor()es sich um eine Klassenmethode handelt, übergibt der Aufruf super(B, cls).alternate_constructor(...)implizit eine Referenz von clsals erstes Argument an Adie Version vonalternate_constructor()

super(B, cls).alternate_constructor()

4- super()Um dasselbe zu tun, ohne es zu verwenden , müssten Sie einen Verweis auf die ungebundene Version von A.alternate_constructor()(dh die explizite Version der Funktion) erhalten. Einfach das zu tun würde nicht funktionieren:

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return A.alternate_constructor(cls, *a, **kw)

Dies würde nicht funktionieren, da die A.alternate_constructor()Methode Aals erstes Argument einen impliziten Verweis auf verwendet . Das hier übergebene clsWesen wäre das zweite Argument.

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        # first we get a reference to the unbound 
        # `A.alternate_constructor` function 
        unbound_func = A.alternate_constructor.im_func
        # now we call it and pass our own `cls` as its first argument
        return unbound_func(cls, *a, **kw)
Michael Ekoka
quelle
6

Viele gute Antworten, aber für visuelle Lernende: Lassen Sie uns zuerst mit Argumenten zu Super und dann ohne erkunden. Beispiel für einen Super-Vererbungsbaum

Stellen Sie sich eine Instanz vor, jackdie aus der Klasse erstellt Jackwurde 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 ab Jack. Warum kann man eine Elternklasse anbieten? Wenn wir von der Instanz jackaus 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 selfund das zweite übergebene Argument ist self. Diese werden in Python3 automatisch für Sie berechnet.

Angenommen, wir möchten die JackMethode nicht verwenden, anstatt sie weiterzugeben Jack, könnten wir sie übergeben Jen, um nach oben nach der Methode zu suchen Jen.

Es durchsucht eine Schicht zu einer Zeit (Breite nicht Tiefe), zB wenn Adamund Suebeide haben die erforderlichen Verfahren, das eine vom Suezuerst gefunden werden.

Wenn Cainund Suebeide die erforderliche Methode hätten, Cainwürde die Methode zuerst aufgerufen. Dies entspricht im Code:

Class Jen(Cain, Sue):

MRO ist von links nach rechts.

run_the_race
quelle
2

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:

  1. Die Klassen der obersten Ebene in Ihrer Hierarchie müssen von einer benutzerdefinierten Klasse wie folgt erben SuperObject:
  2. Wenn Klassen unterschiedliche Argumente annehmen können, übergeben Sie immer alle Argumente, die Sie erhalten haben, als Schlüsselwortargumente an die Superfunktion und akzeptieren Sie immer **kwargs.
class SuperObject:        
    def __init__(self, **kwargs):
        print('SuperObject')
        mro = type(self).__mro__
        assert mro[-1] is object
        if mro[-2] is not SuperObject:
            raise TypeError(
                'all top-level classes in this hierarchy must inherit from SuperObject',
                'the last class in the MRO should be SuperObject',
                f'mro={[cls.__name__ for cls in mro]}'
            )

        # super().__init__ is guaranteed to be object.__init__        
        init = super().__init__
        init()

Anwendungsbeispiel:

class A(SuperObject):
    def __init__(self, **kwargs):
        print("A")
        super(A, self).__init__(**kwargs)

class B(SuperObject):
    def __init__(self, **kwargs):
        print("B")
        super(B, self).__init__(**kwargs)

class C(A):
    def __init__(self, age, **kwargs):
        print("C",f"age={age}")
        super(C, self).__init__(age=age, **kwargs)

class D(B):
    def __init__(self, name, **kwargs):
        print("D", f"name={name}")
        super(D, self).__init__(name=name, **kwargs)

class E(C,D):
    def __init__(self, name, age, *args, **kwargs):
        print( "E", f"name={name}", f"age={age}")
        super(E, self).__init__(name=name, age=age, *args, **kwargs)

E(name='python', age=28)

Ausgabe:

E name=python age=28
C age=28
A
D name=python
B
SuperObject
Aviad Rozenhek
quelle
0
class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

Das ist ziemlich leicht zu verstehen.

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

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, selfwird superim MRO der selfInstanz 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 ist super(SomeBaseClass,self), ist das zurückgegebene Proxy-Objektsuperobject

Bei der Mehrfachvererbung kann die MRO viele Klassen enthalten, sodass Sie im Grunde superentscheiden können, wo Sie mit der Suche in der MRO beginnen möchten.

dorisxx
quelle
0

Betrachten Sie den folgenden Code:

class X():
    def __init__(self):
        print("X")

class Y(X):
    def __init__(self):
        # X.__init__(self)
        super(Y, self).__init__()
        print("Y")

class P(X):
    def __init__(self):
        super(P, self).__init__()
        print("P")

class Q(Y, P):
    def __init__(self):
        super(Q, self).__init__()
        print("Q")

Q()

Wenn Sie den Konstruktor von Yto ändern X.__init__, erhalten Sie:

X
Y
Q

Aber super(Y, self).__init__()wenn Sie verwenden , erhalten Sie:

X
P
Y
Q

Und Poder Qkann sogar aus einer anderen Datei beteiligt sein, die Sie beim Schreiben nicht kennen Xund Y. Im Grunde wissen Sie also nicht, worauf super(Child, self)Sie sich beim Schreiben beziehen class Y(X), selbst die Signatur von Y ist so einfach wie Y(X). Deshalb könnte Super eine bessere Wahl sein.

tsh
quelle