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.
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 wiee = Event(description="stored the current time")
sollte eine Instanz der UnterklasseEvent
des Tupels(1653520485,"stored...")
. Aber in__init__()
Ich kann nicht ändernself
wie ich es für eine Unterklasse vondict
. Also dachte ich, ich könnte diesuper().__init__
um die Komponenten des Tupels zu setzenself
. Darf ich?