Frage

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 super gesehen eine ganze Menge in Klassen mit nur einfacher Vererbung wird verwendet. Ich kann sehen, warum Sie es in Mehrfachvererbung verwenden würde, aber ich bin nicht klar, was die Vorteile sind es in dieser Situation verwendet wird.

War es hilfreich?

Lösung

Die Vorteile von super() in Single-Vererbung sind minimal - meist, Sie müssen nicht zu hart Code den Namen der Basisklasse in jede Methode, die ihre Mutter Methoden verwendet,

.

Allerdings ist es fast unmöglich, Mehrfachvererbung ohne super() zu verwenden. Dazu gehören gemeinsame Idiome wie Mixins, Schnittstellen, abstrakte Klassen, etc. Dies, um Code erweitert, die Ihnen später erweitert. Wenn jemand später wollte eine Klasse schreiben, die Child und eine mixin verlängert, würde ihr Code nicht richtig funktionieren.

Andere Tipps

Was ist der Unterschied?

SomeBaseClass.__init__(self) 

bedeutet SomeBaseClass des __init__ zu nennen. während

super(Child, self).__init__()

bedeutet ein gebundenes __init__ von der Elternklasse aufzurufen, die Child in der Instanz Methode Resolution Order (MRO) folgt.

Wenn die Instanz eine Unterklasse des Kindes ist, kann es ein anderer Elternteil sein, die in der MRO als nächstes kommt.

einfach erklärt

Wenn Sie eine Klasse schreiben, mögen Sie andere Klassen der Lage sein, es zu benutzen. super() macht es einfacher für andere Klassen, die Klasse verwenden Sie schreiben.

Wie Bob Martin sagt, eine gute Architektur ermöglicht es Ihnen, Entscheidungsfindung so lange wie möglich zu verschieben.

super() kann diese Art von Architektur ermöglichen.

Wenn eine andere Klasse, die Klasse Unterklassen Sie schrieb, könnte es auch aus anderen Klassen erben. Und diese Klassen könnten eine __init__ haben, die für die Methode Auflösung basierend auf der Reihenfolge der Klassen nach dieser __init__ kommt.

Ohne super würden Sie wahrscheinlich hart Code die Eltern der Klasse Sie schreiben (wie das Beispiel der Fall ist). Dies würde bedeuten, dass Sie sich den nächsten __init__ im MRO nicht nennen, und Sie erhalten würden somit nicht den Code in ihm wieder zu verwenden.

Wenn Sie Ihren eigenen Code für den persönlichen Gebrauch zu schreiben, können Sie diesen Unterschied nicht. Aber wenn Sie andere wollen Ihren Code verwenden, super verwendet, ist eine Sache, die für die Nutzer des Codes mehr Flexibilität ermöglicht.

Python 2 im Vergleich zu 3

Dies funktioniert in Python 2 und 3:

super(Child, self).__init__()

Dies funktioniert nur in Python 3:

super().__init__()

Es ohne Argumente wirkt im Stapelrahmen und immer das erste Argumente der Methode nach oben bewegt (in der Regel für eine Instanz-Methode oder self für eine Klassenmethode cls - könnte aber andere Namen sein) und die Suche nach der Klasse (zB Child ) in den freien Variablen (es wird nachgeschlagen mit dem Namen __class__ als freier Verschluss Variable in der Methode).

Ich ziehe den Quer kompatibel Art der Verwendung super zu demonstrieren, aber wenn Sie nur Python 3 verwenden, können Sie es ohne Argumente aufrufen.

Indirection mit Vorwärts-Kompatibilität

Was bedeutet es Ihnen? Für die einfache Vererbung sind die Beispiele aus der Frage praktisch identisch aus einer statischen Analyse Sicht. Allerdings super Verwendung gibt Ihnen eine Schicht von Dereferenzierung mit Vorwärtskompatibilität.

Aufwärtskompatibilität ist sehr wichtig für erfahrene Entwickler. Sie wollen Ihren Code mit minimalen Änderungen weiter zu arbeiten, wie Sie es ändern. Wenn Sie an Ihrer Revision Geschichte betrachten, wollen Sie genau sehen, was, wenn geändert.

Sie können mit einfacher Vererbung beginnen, aber wenn Sie eine andere Basisklasse hinzufügen entscheiden, Sie müssen nur mit den Basen der Linie ändern - wenn die Basen in einer Klasse ändern erben Sie von (sagen wir ein mixin hinzugefügt wird) Sie Würd nichts in dieser Klasse ändern. Vor allem in Python 2, um die Argumente immer super und die richtige Methode Argumente richtig schwierig sein. Wenn Sie wissen, dass Sie super richtig mit einfacher Vererbung verwenden, das macht das Debuggen weniger schwer vorwärts gehen.

Dependency Injection

Andere Personen können Sie Ihren Code und zu injizieren Eltern in die Methode Auflösung verwenden:

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__()

Sagen Sie bitte eine andere Klasse zu Ihrem Objekt hinzufügen, und wollen eine Klasse zwischen Foo und Bar (zum Testen oder aus einem anderen Grund) injizieren:

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

Mit dem un-Super Kind schlägt die Abhängigkeit injizieren, weil das Kind verwenden Sie hartcodiert die Methode muss nach seinem eigenen aufgerufen werden:

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

Allerdings ist die Klasse mit dem Kind, das super verwendet, kann die Abhängigkeit richtig injizieren:

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

Addressing ein Kommentar

Warum in der Welt würde dies nützlich sein?

Python linearisiert einen komplizierten Vererbungsbaum über das C3 Linearisierungsalgorithmus eine Methode Auflösung zu schaffen ( MRO).

Wir wollen Methoden nachgeschlagen werden in dieser Reihenfolge .

Für ein Verfahren in einem Elternteil definiert die nächste in dieser Reihenfolge ohne super zu finden, wäre es zu

haben
  1. erhalten die MRO von der Instanz Typ
  2. Look für den Typ, der die Methode
  3. definiert
  4. Sie den nächsten Typen mit der Methode
  5. diese Methode binden und sie mit den erwarteten Argumente nennen
  

Die UnsuperChild sollte nicht den Zugang zu InjectMe haben. Warum ist nicht der Schluss zu „vermeiden Immer super mit“? Was ich hier fehlt?

Die UnsuperChild tun nicht Zugang zu InjectMe hat. Es ist die UnsuperInjector, die Zugriff auf InjectMe hat - und doch nicht auf diese Klasse Methode aus dem Verfahren aus UnsuperChild erbt aufrufen können.

Die beiden Kinderklassen beabsichtigen, ein Verfahren, mit dem gleichen Namen zu nennen, die in den MRO nächsten kommt, die möglicherweise andere Klasse es nicht bewusst war, als es erstellt wurde.

Das eine ohne super hart Codes seiner Eltern Methode -. So hat das Verhalten seiner Methode beschränkt, und Subklassen kann nicht Funktionalität in der Aufrufkette injizieren

Die eine mit super hat eine größere Flexibilität. Die Verbindungskette für die Methoden abgefangen werden kann und Funktionalität injiziert.

Sie können nicht diese Funktionalität benötigen, aber subclassers des Codes kann.

Fazit

Immer super verwenden die übergeordnete Klasse statt Hartcodierung es zu verweisen.

Was Sie beabsichtigen, ist die übergeordnete Klasse zu verweisen, die nächsten in-line ist, nicht speziell die, die Sie das Kind sehen zu erben von.

Nicht super verwenden, können unnötige Einschränkungen für Benutzer des Codes setzen.

Ist dies nicht alle davon ausgehen, dass die Basisklasse eine neuen Stil-Klasse ist?

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

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

Werde nicht 2. class A in Python arbeiten müssen neuen Stil sein, das heißt: class A(object)

ich ein wenig mit super() gespielt hatte, und hatte erkannt, dass wir fordern, um zu ändern.

Zum Beispiel, wir haben nächste Hierarchiestruktur:

    A
   / \
  B   C
   \ /
    D

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

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

Lassen Sie uns eine Klasse erstellen, wo super() nach Ausführung der Methode aufruft.

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

So können wir die Auflösung ist, um zu sehen gleiche wie in MRO. Aber wenn wir super() am Anfang des Verfahrens nennen:

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 eine Reihenfolge des MRO Tupel umgekehrt wird.

    A
   / ⇘
  B ⇐ C
   ⇘ /
    D 

Für weitere Lesung würde ich nächste Antworten empfehlen:

  1. C3 Linearisierungs Beispiel mit Super (eine große Hierarchie)
  2. Wichtige Verhaltensänderungen zwischen alten und neuen Stilklassen
  3. Die Inside Story auf Neu- Style-Klassen

Wenn super() zu einem Eltern Version eines Class, Instanzmethode oder static zu lösen fordern, wollen wir die aktuelle Klasse, deren Umfang wir sind in als erstes Argument übergeben, um anzuzeigen, welche Eltern Umfang wir versuchen zu lösen zu, und als zweites Argument das Objekt von Interesse, um anzuzeigen, welches Objekt wir diesen Umfang zu.

anzuwenden sind versuchen,

Betrachten wir eine Klassenhierarchie A, B und C wobei jede Klasse der Elternteil des einen ist es folgende, und a, b und c jeweiligen 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

Mit super mit einem static

z. mit super() aus der __new__() Methode

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

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

Erklärung:

1- obwohl es üblich ist für __new__() als erstes param einen Verweis auf die Berufung Klasse zu nehmen, ist es nicht implementiert in Python als Class, sondern ein static. Das heißt, hat ein Verweis auf eine Klasse explizit als erstes Argument übergeben wird beim Aufruf __new__() direkt:

# 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() Aufruf zu der übergeordneten Klasse bekommen wir das Kind Klasse A als erstes Argument übergeben, dann geben wir eine Referenz auf das Objekt von Interesse, in diesem Fall ist es die Klassenreferenz, die vergangen war, als A.__new__(cls) genannt wurde . In den meisten Fällen kommt es auch ein Verweis auf das Kind Klasse. In manchen Situationen kann es nicht sein, zum Beispiel im Fall von mehrere Generation Erbschaften.

super(A, cls)

3-, da sie in der Regel __new__() ein static ist, super(A, cls).__new__ wird auch eine static zurückkehren und muss alle Argumente explizit geliefert werden, einschließlich der Referenz auf das Objekt von insterest, in diesem Fall cls.

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

4- dasselbe ohne super tun

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

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

Mit super mit einer Instanzmethode

z. mit super() aus __init__()

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

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

Erklärung:

1- __init__ ist eine Instanzmethode, was bedeutet, dass sie als erstes Argument zu einer Instanz eine Referenz nimmt. Wenn direkt von der Instanz aufgerufen, die Referenz implizit übergeben wird, dh Sie müssen es 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- beim Aufruf super() innerhalb __init__() sie das Kind Klasse als erstes Argument und das interessierende Objekt als zweites Argument übergeben, die in der Regel ist ein Verweis auf eine Instanz der Kind Klasse.

super(A, self)

3- Der Aufruf super(A, self) gibt einen Proxy, der den Umfang und die Anwendung wird es lösen self, als ob es nun eine Instanz der übergeordneten Klasse. Lassen Sie uns diesen Proxy s nennen. Da __init__() eine Instanzmethode ist, wird der Anruf s.__init__(...) implizit einen Verweis von self als erstes Argument an die __init__() der Eltern übergeben.

4-, das gleiche zu tun, ohne super brauchen wir einen Verweis auf eine Instanz, um die Version der Eltern von __init__() explizit zu übergeben.

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

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

Mit super mit einem Class

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)

Erklärung:

1- A Class kann aus der Klasse aufgerufen werden direkt und nimmt 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- beim Aufruf super() innerhalb eines Class seiner Eltern Version davon zu lösen, wollen wir die aktuelle Kind Klasse als erstes Argument übergeben, um anzuzeigen, welche Eltern Umfang wir zu lösen sind, und lassen den Gegenstand des Interesses als das zweite Argument, um anzuzeigen, welches Objekt wir diesen Umfang zu, die im allgemeinen ist ein Verweis auf das Kind Klasse anwenden möchtenselbst oder einer ihrer Unterklassen.

super(B, cls_or_subcls)

3- Der Aufruf super(B, cls) löst den Umfang der A und wendet sie auf cls. Da alternate_constructor() ein Class ist, wird der Anruf super(B, cls).alternate_constructor(...) implizit einen Verweis von cls als erstes Argument zu A Version von alternate_constructor() geben

super(B, cls).alternate_constructor()

4-, das gleiche zu tun, ohne super() verwenden müßten Sie einen Verweis auf die ungebundene Version von A.alternate_constructor() erhalten (das heißt die explizite Version der Funktion). Einfach dies 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)

Das oben würde nicht funktionieren, weil die A.alternate_constructor() Methode eine implizite Referenz nimmt als erstes Argument A. Die cls hier weitergegeben werden würde sein zweites Argument sein.

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)
class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

Das ist ziemlich einfach zu verstehen.

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

Ok, was passiert jetzt, wenn Sie super(Child,self) verwenden?

Wenn ein Kind Instanz erstellt wird, seine MRO (Methode Resolution Order) in der Größenordnung von (Child, Somebaseclass, Objekt) auf der Vererbung basiert. (Unter der Annahme Somebaseclass hat keine andere Eltern mit Ausnahme des Standardobjekt)

Durch die Übergabe Child, self, super sucht im HRG der self Instanz, und senden Sie das Proxy-Objekt nächsten Kind, in diesem Fall ist es Somebaseclass ist, ruft das Objekt dann die __init__ Methode Somebaseclass. Mit anderen Worten, wenn es super(SomeBaseClass,self) ist, dass das Proxy-Objekt zurückkehrt super würde object

Für Multi Vererbung, die MRO viele Klassen enthalten könnte, so dass im Grunde super können Sie entscheiden, wo Sie in der MRO-Suche gestartet werden soll.

einige großen Antworten hier, aber sie bekämpfen nicht, wie super() in dem Fall zu verwenden, in denen verschiedenen Klassen in der Hierarchie unterschiedliche Signaturen haben ... vor allem im Fall von __init__

, dass ein Teil zu beantworten und in der Lage sein, effektiv super() zu verwenden, ich meine Antwort lesen würde vorschlagen, super () und die Änderung der Unterschrift von kooperativen Methoden .

Hier ist nur die Lösung für dieses Szenario:

  
      
  1. die Top-Level-Klassen in der Hierarchie von einer benutzerdefinierten Klasse wie SuperObject erben müssen:
  2.   
  3. , wenn Klassen nehmen Argumente unterscheiden, passieren immer alle Argumente, die Sie auf die Super-Funktion als Schlüsselwort-Argumente erhalten, und, immer **kwargs nehmen.
  4.   
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()

Beispiel für die Verwendung:

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

Viele große Antworten, aber für visuelle Lerner: Zum einen können mit Argumenten zu Super erkunden, und dann ohne. Super Vererbungsbaum Beispiel

Stellen Sie sich vor Theres einer Instanz jack aus der Klasse Jack erstellt, der die Vererbungskette hat wie in grün in der Abbildung dargestellt. Berufung:

super(Jack, jack).method(...)

wird den MRO (Methode Resolution Order) von jack (seinen Vererbungsbaum in einer bestimmten Reihenfolge) verwenden, und beginnt von Jack suchen. Warum kann man eine Elternklasse zur Verfügung stellen? Nun, wenn wir von der Instanz jack starten Sie die Suche, würde es die Instanzmethode finden, der springende Punkt ist seine Eltern Methode zu finden.

Wenn man nicht Argumente Super liefern, es ist wie das erste übergebene Argument ist die Klasse der self, und das zweite Argument übergeben ist self. Diese werden automatisch berechnet für Sie in Python3.

Allerdings sagen wir nicht wollen, Jack Methode verwenden, anstatt in Jack von vorbei, wir in Jen bestanden von könnte nach oben für das Verfahren von Jen die Suche zu starten.

Es sucht eine Schicht zu einer Zeit (Breite nicht Tiefe), z.B. wenn Adam und Sue beide haben die erforderlichen Verfahren, die man von Sue zuerst gefunden werden.

Wenn Cain und Sue beide hatten die gewünschte Methode, Cain Methode würde zuerst genannt werden. Dies entspricht im Code:

Class Jen(Cain, Sue):

MRO wird von links nach rechts.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top