3215 Stimmen

Verständnis von Python super() mit __init__() Methoden

Warum ist super() verwendet?

Gibt es einen Unterschied zwischen der Verwendung von Base.__init__ y super().__init__ ?

class Base(object):
    def __init__(self):
        print "Base created"

class ChildA(Base):
    def __init__(self):
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        super(ChildB, self).__init__()

ChildA() 
ChildB()

31 Stimmen

Dies ist eine sehr einfache Einführung in den Unterricht, die es wert ist, durchgenommen zu werden: realpython.com/python-super/ . Es ist leichter zu verdauen als die gegebenen Antworten, die für die meisten von uns vermutlich zu detailliert in der Implementierung von Python sind. Es hat auch Beispiele, um es konkret zu machen.

0 Stimmen

Ich habe es immer noch nicht verstanden. Ich möchte eine class Event(tuple) die Tupel (Zeitstempel, Beschreibung) erstellt, wobei der Zeitstempel standardmäßig die aktuelle Zeit sein sollte. Also, etwas wie e = Event(description="stored the current time") sollte eine Instanz der Unterklasse Event des Tupels (1653520485,"stored...") . Aber in __init__() Ich kann nicht ändern self wie ich es für eine Unterklasse von dict . Also dachte ich, ich könnte die super().__init__ um die Komponenten des Tupels zu setzen self . Darf ich?

2328voto

Kiv Punkte 30274

super() können Sie vermeiden, explizit auf die Basisklasse zu verweisen, was sehr nützlich sein kann. Der größte Vorteil ergibt sich jedoch bei der Mehrfachvererbung, bei der alle Arten von lustige Sachen passieren kann. Siehe die Standarddokumente auf Super wenn Sie es nicht schon getan haben.

Beachten Sie, dass die Syntax wurde in Python 3.0 geändert : Sie können einfach sagen super().__init__() 代わりに super(ChildB, self).__init__() was IMO ein bisschen schöner ist. Die Standarddokumente verweisen auch auf eine Leitfaden zur Verwendung super() was recht aussagekräftig ist.

55 Stimmen

Können Sie ein Beispiel nennen für super() mit Argumenten verwendet werden?

30 Stimmen

Können Sie bitte erklären super(ChildB, self).__init__() dies, was bedeutet ChildB y self haben mit dem Super zu tun

13 Stimmen

@rimiro Die Syntax von super() lautet super([type [, object]]) Dies gibt die Oberklasse von type . In diesem Fall ist also die Oberklasse von ChildB zurückgegeben werden. Wird das zweite Argument weggelassen, ist das zurückgegebene Superobjekt ungebunden. Wenn das zweite Argument ein Objekt ist, dann isinstance(object, type) muss wahr sein.

1244voto

Ich versuche zu verstehen super()

Der Grund für die Verwendung von super ist, damit untergeordnete Klassen, die möglicherweise kooperative Mehrfachvererbung verwenden, die richtige nächste Funktion der übergeordneten Klasse in der Methodenauflösungsreihenfolge (MRO) aufrufen.

In Python 3 können wir es wie folgt aufrufen:

class ChildB(Base):
    def __init__(self):
        super().__init__()

In Python 2 waren wir verpflichtet, die super wie hier mit dem Namen der definierenden Klasse und self aber wir werden dies von nun an vermeiden, weil es redundant, langsamer (wegen der Namenssuche) und ausführlicher ist (aktualisieren Sie also Ihr Python, falls Sie es noch nicht getan haben!):

        super(ChildB, self).__init__()

Ohne Super sind Sie in Ihren Möglichkeiten der Mehrfachvererbung eingeschränkt, da Sie den Aufruf des nächsten Elternteils fest verdrahten:

        Base.__init__(self) # Avoid this.

Ich erkläre dies weiter unten.

"Welchen Unterschied gibt es eigentlich in diesem Code?"

class ChildA(Base):
    def __init__(self):
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        super().__init__()

Der Hauptunterschied in diesem Code besteht darin, dass in ChildB erhalten Sie eine indirekte Ebene in der __init__ avec super die die Klasse verwendet, in der sie definiert ist, um die nächste Klasse zu bestimmen __init__ in der MRO nachzuschlagen.

Ich veranschauliche diesen Unterschied in einer Antwort auf die kanonische Frage, Wie verwendet man 'super' in Python? die zeigt, dass Dependency Injection y kooperative Mehrfachvererbung .

Wenn Python nicht über super

Hier ist ein Code, der in etwa gleichwertig ist mit super (wie es in C implementiert ist, abzüglich einiger Überprüfungen und Fallback-Verhalten, und übersetzt in Python):

class ChildB(Base):
    def __init__(self):
        mro = type(self).mro()
        check_next = mro.index(ChildB) + 1 # next after *this* class.
        while check_next < len(mro):
            next_class = mro[check_next]
            if '__init__' in next_class.__dict__:
                next_class.__init__(self)
                break
            check_next += 1

Ein wenig mehr wie natives Python geschrieben:

class ChildB(Base):
    def __init__(self):
        mro = type(self).mro()
        for next_class in mro[mro.index(ChildB) + 1:]: # slice to end
            if hasattr(next_class, '__init__'):
                next_class.__init__(self)
                break

Hätten wir nicht die super Objekts müssten wir diesen manuellen Code überall schreiben (oder ihn neu erstellen!), um sicherzustellen, dass wir die richtige nächste Methode in der Methodenauflösungsreihenfolge aufrufen!

Wie tut super dies in Python 3, ohne explizit gesagt werden, welche Klasse und Instanz aus der Methode es aufgerufen wurde?

Er holt sich den aufrufenden Stack-Frame und findet die Klasse (implizit als lokale freie Variable gespeichert), __class__ (wodurch die aufrufende Funktion zu einer Schließung über die Klasse wird) und das erste Argument dieser Funktion, bei dem es sich um die Instanz oder Klasse handeln sollte, die ihr mitteilt, welche Methodenauflösungsreihenfolge (MRO) zu verwenden ist.

Da dieses erste Argument für die MRO erforderlich ist, mit super mit statischen Methoden ist nicht möglich, da sie keinen Zugriff auf die MRO der Klasse haben, aus der sie aufgerufen werden .

Kritikpunkte an anderen Antworten:

Mit super() können Sie es vermeiden, sich explizit auf die Basisklasse zu beziehen, was sehr nützlich sein kann. Der Hauptvorteil ergibt sich jedoch bei der Mehrfachvererbung, wo alle möglichen lustigen Dinge passieren können. Sehen Sie sich die Standarddokumentation zu super an, falls Sie das noch nicht getan haben.

Es ist eher schwammig und sagt uns nicht viel, aber der Sinn der super ist nicht, um das Schreiben der übergeordneten Klasse zu vermeiden. Es geht darum, sicherzustellen, dass die nächste Methode in der Reihenfolge der Methodenauflösung (MRO) aufgerufen wird. Dies wird bei der Mehrfachvererbung wichtig.

Ich werde das hier erklären.

class Base(object):
    def __init__(self):
        print("Base init'ed")

class ChildA(Base):
    def __init__(self):
        print("ChildA init'ed")
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        print("ChildB init'ed")
        super().__init__()

Und lassen Sie uns eine Abhängigkeit erstellen, die nach dem Child aufgerufen werden soll:

class UserDependency(Base):
    def __init__(self):
        print("UserDependency init'ed")
        super().__init__()

Denken Sie daran, ChildB benutzt Super, ChildA nicht:

class UserA(ChildA, UserDependency):
    def __init__(self):
        print("UserA init'ed")
        super().__init__()

class UserB(ChildB, UserDependency):
    def __init__(self):
        print("UserB init'ed")
        super().__init__()

Und UserA ruft die Methode UserDependency nicht auf:

>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>

Aber UserB ruft in der Tat UserDependency auf, weil ChildB ruft auf. super :

>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>

Kritik für eine andere Antwort

Auf keinen Fall sollten Sie das Folgende tun, was eine andere Antwort vorschlägt, da Sie auf jeden Fall Fehler erhalten werden, wenn Sie ChildB unterklassifizieren:

super(self.__class__, self).__init__()  # DON'T DO THIS! EVER.

(Diese Antwort ist weder klug noch besonders interessant, aber trotz direkter Kritik in den Kommentaren und über 17 Ablehnungen blieb der Antwortende bei seinem Vorschlag, bis ein freundlicher Redakteur sein Problem behoben hatte).

Erläuterung: Unter self.__class__ als Ersatz für den Klassennamen in super() zu einer Rekursion führen wird. super ermöglicht es uns, den nächsten Elternteil im MRO (siehe erster Abschnitt dieser Antwort) für untergeordnete Klassen nachzuschlagen. Wenn Sie sagen super Wenn wir uns in der Methode der Kindinstanz befinden, wird die nächste Methode in der Zeile (wahrscheinlich diese) aufgerufen, was zu einer Rekursion führt und wahrscheinlich einen logischen Fehler verursacht (im Beispiel des Antwortenden ist das der Fall) oder einen RuntimeError wenn die Rekursionstiefe überschritten wird.

>>> class Polygon(object):
...     def __init__(self, id):
...         self.id = id
...
>>> class Rectangle(Polygon):
...     def __init__(self, id, width, height):
...         super(self.__class__, self).__init__(id)
...         self.shape = (width, height)
...
>>> class Square(Rectangle):
...     pass
...
>>> Square('a', 10, 10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'

Das neue Python 3 super() Der Aufruf einer Methode ohne Argumente ermöglicht es uns glücklicherweise, dieses Problem zu umgehen.

64 Stimmen

Ich muss mir das erst noch durch den Kopf gehen lassen. super() Funktion, aber diese Antwort ist eindeutig die beste in Bezug auf Tiefe und Details. Ich schätze auch die Kritikpunkte innerhalb der Antwort sehr. Sie helfen auch, das Konzept besser zu verstehen, indem sie die Fallstricke in anderen Antworten aufzeigen. Ich danke Ihnen!

4 Stimmen

@Aaron Hall, danke für die ausführlichen Informationen. Ich denke, es sollte (zumindest) eine weitere Option für Mentoren geben, um einige Antworten als unangemessen oder unvollständig zu bezeichnen, wenn sie keine ausreichenden und korrekten Informationen liefern.

7 Stimmen

Danke, das war sehr hilfreich. Die Kritik an der schlechten/falschen Verwendung war sehr anschaulich, warum, und wie man super

282voto

AnjoMan Punkte 4680

Es wurde festgestellt, dass man in Python 3.0+ die Funktion

super().__init__()

um Ihren Aufruf zu tätigen, was kurz und bündig ist und nicht erfordert, dass Sie explizit auf die Namen der übergeordneten ODER der Klassen verweisen, was praktisch sein kann. Ich möchte nur hinzufügen, dass einige Leute für Python 2.7 oder darunter ein namensunabhängiges Verhalten implementieren, indem sie schreiben self.__class__ anstelle des Klassennamens, d. h.

super(self.__class__, self).__init__()  # DON'T DO THIS!

JEDOCH unterbricht dies die Aufrufe zu super für alle Klassen, die von Ihrer Klasse erben, wobei self.__class__ könnte eine Kindklasse zurückgeben. Zum Beispiel:

class Polygon(object):
    def __init__(self, id):
        self.id = id

class Rectangle(Polygon):
    def __init__(self, id, width, height):
        super(self.__class__, self).__init__(id)
        self.shape = (width, height)

class Square(Rectangle):
    pass

Hier habe ich eine Klasse Square , die eine Unterklasse von Rectangle . Angenommen, ich möchte keinen separaten Konstruktor schreiben für Square weil der Konstruktor für Rectangle ist gut genug, aber aus irgendeinem Grund möchte ich ein Square implementieren, damit ich eine andere Methode neu implementieren kann.

Wenn ich eine Square mit mSquare = Square('a', 10,10) auf, ruft Python den Konstruktor für Rectangle denn ich habe nicht gegeben Square seinen eigenen Konstruktor. In dem Konstruktor für Rectangle der Aufruf super(self.__class__,self) wird die Oberklasse von mSquare und ruft daher den Konstruktor für Rectangle wieder. Auf diese Weise entsteht die von @S_C erwähnte Endlosschleife. In diesem Fall, wenn ich super(...).__init__() Ich rufe den Konstruktor für Rectangle aber da ich ihm keine Argumente gebe, erhalte ich einen Fehler.

46 Stimmen

Was diese Antwort nahelegt, super(self.__class__, self).__init__() funktioniert nicht, wenn Sie erneut eine Unterklasse bilden, ohne eine neue __init__ . Dann haben Sie eine unendliche Rekursion.

26 Stimmen

Diese Antwort ist lächerlich. Wenn Sie Super auf diese Weise missbrauchen wollen, können Sie den Namen der Basisklasse auch einfach hart codieren. Das ist weniger falsch als das hier. Der ganze Sinn des ersten Arguments von super ist, dass es pas notwendigerweise der Typ des Selbst. Bitte lesen Sie "Super als Super" von Rhettinger (oder schauen Sie sich einige seiner Videos an).

7 Stimmen

Die hier demonstrierte Abkürzung für Python 2 hat die bereits erwähnten Tücken. Verwenden Sie sie nicht, oder Ihr Code wird auf unvorhersehbare Weise kaputtgehen. Diese "praktische Abkürzung" ist super, aber Sie merken es vielleicht erst, wenn Sie viel Zeit in die Fehlersuche gesteckt haben. Verwenden Sie Python 3, wenn super zu langatmig ist.

83voto

S C Punkte 893

Super hat keine Nebenwirkungen

Base = ChildB

Base()

funktioniert wie erwartet

Base = ChildA

Base()

in eine unendliche Rekursion gerät.

8 Stimmen

Die Aussage "Super hat keine Nebenwirkungen" macht in diesem Zusammenhang keinen Sinn. Super garantiert lediglich, dass wir die korrekte Methode der nächsten Klasse in der Reihenfolge der Methodenauflösung aufrufen, während auf dem anderen Weg die nächste aufzurufende Methode hart kodiert wird, was die kooperative Mehrfachvererbung erschwert.

6 Stimmen

Diese Antwort ist fragmentarisch (Codebeispiele sind nur als Fortsetzung des Codes aus der Antwort sinnvoll).

77voto

rgenito Punkte 1691

Nur eine Vorwarnung... mit Python 2.7, und ich glaube, seitdem super() in Version 2.2 eingeführt wurde, können Sie nur noch super() wenn einer der Elternteile von einer Klasse erbt, die schließlich erbt object ( Klassen im neuen Stil ).

Was den Python 2.7 Code angeht, so werde ich persönlich weiterhin BaseClassName.__init__(self, args) bis ich tatsächlich den Vorteil der Verwendung von super() .

5 Stimmen

Sehr guter Punkt. WENN Sie nicht eindeutig erwähnen: class Base(object): dann werden Sie einen Fehler wie diesen erhalten: "TypeError: must be type, not classobj"

1 Stimmen

@andi Ich hatte diese Fehlermeldung neulich auch und habe es schließlich aufgegeben, nach einer Lösung zu suchen. Ich habe gerade mit iPython herumgespielt. Was für ein verdammter Albtraum einer schlechten Fehlermeldung, wenn das tatsächlich Code war, den ich debuggen musste!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X