Wenn wir vonsupersprechen, könnte jeder denken, es sei sehr einfach – wird es nicht einfach verwendet, um Methoden der Elternklasse aufzurufen? Wenn es wirklich so einfach wäre, gäbe es diesen Artikel nicht. Lassen Sie es mich ausführlich erklären.
Konventionen
Bevor wir beginnen, einigen wir uns auf die in diesem Artikel verwendete Python-Version. Standardmäßig verwenden wir Python 3, was bedeutet, dass alle in diesem Artikel definierten Klassen New-Style-Klassen sind. Wenn Sie Python 2 verwenden, denken Sie daran, vonobject zu erben:
# 默认, Python 3
class A:
pass
# Python 2
class A(object):
pass
Ein weiterer Unterschied zwischen Python 3 und Python 2 ist, dass Python 3 direktsuper().xxxanstelle vonsuper(Class, self).xxx verwenden kann :
# 默认,Python 3
class B(A):
def add(self, x):
super().add(x)
# Python 2
class B(A):
def add(self, x):
super(B, self).add(x)
Wenn Sie also Python 2 verwenden, denken Sie daran, dassuper()im Artikel durchsuepr(Class, self) zu ersetzen 。
Wenn es weitere Fälle gibt, die mit Python 2 nicht kompatibel sind, werde ich sie im Artikel vermerken.
Einfachvererbung
Bei der Einfachvererbung wirdsuper, wie allgemein angenommen, hauptsächlich verwendet, um Methoden der Elternklasse aufzurufen.
class A:
def __init__(self):
self.n = 2
def add(self, m):
print('self is {0} @A.add'.format(self))
self.n += m
class B(A):
def __init__(self):
self.n = 3
def add(self, m):
print('self is {0} @B.add'.format(self))
super().add(m)
self.n += 3
Was denken Sie, wird der Wert von b.nnach Ausführung des folgenden Codes sein?
b = B() b.add(2) print(b.n)
Das Ausführungsergebnis ist wie folgt:
self is <__main__.B object at 0x106c49b38> @B.add self is <__main__.B object at 0x106c49b38> @A.add 8
Dieses Ergebnis veranschaulicht zwei Punkte:
- 1, super().add(m)Es ruft tatsächlich dieadd-Methode der Elternklasse A auf.
- 2, super().add(m)Beim Aufruf der Methode der Elternklassedef add(self, m), ist zu diesem Zeitpunkt in der Elternklasseselfkeine Instanz der Elternklasse, sondern eine Instanz der Kindklasse, alsob.add(2)Das Ergebnis danach ist5statt4 。
Ich frage mich, ob dieses Ergebnis dasselbe ist wie das, was Sie erwartet haben? Schauen wir uns als Nächstes ein Beispiel für Mehrfachvererbung an.
Mehrfachvererbung
Dieses Mal definieren wir eine weitereKlasse Cund eineKlasse D:
class C(A):
def __init__(self):
self.n = 4
def add(self, m):
print('self is {0} @C.add'.format(self))
super().add(m)
self.n += 4
class D(B, C):
def __init__(self):
self.n = 5
def add(self, m):
print('self is {0} @D.add'.format(self))
super().add(m)
self.n += 5
Was gibt der folgende Code aus?
d = D() d.add(2) print(d.n)
Diesmal ist die Ausgabe wie folgt:
self is <__main__.D object at 0x10ce10e48> @D.add self is <__main__.D object at 0x10ce10e48> @B.add self is <__main__.D object at 0x10ce10e48> @C.add self is <__main__.D object at 0x10ce10e48> @A.add 19
Haben Sie es richtig hinbekommen? Vielleicht denken Sie, die Ausgabe des obigen Codes würde etwa so aussehen:
self is <__main__.D object at 0x10ce10e48> @D.add self is <__main__.D object at 0x10ce10e48> @B.add self is <__main__.D object at 0x10ce10e48> @A.add 15
Warum unterscheidet es sich von dem, was erwartet wurde? Schauen wir uns gemeinsam das Geheimnis vonsuperan.
super ist eine Klasse
Wenn wirsuper()aufrufen, instanziieren wir tatsächlich einesuperKlasse. Du hast richtig gelesen,superist eine Klasse, weder ein Schlüsselwort noch eine Funktion oder eine andere Datenstruktur:
>>> class A: pass ... >>> s = super(A) >>> type(s) <class 'super'> >>>
In den meisten Fällen,superenthält zwei sehr wichtige Informationen: eine MRO und eine Klasse in dieser MRO. Wenn auf folgende Weise aufgerufensuper:
super(a_type, obj)
MRObezieht sich auf die MRO vontype(obj), und die Klasse in der MRO ista_type, und gleichzeitigisinstance(obj, a_type) == True 。
Wenn so aufgerufen:
super(type1, type2)
Die MRO verweist auftype2vonMRO, MROdie Klasse darin isttype1, und gleichzeitigissubclass(type2, type1) == True 。
Also,super()was macht es eigentlich? Einfach gesagt: Es liefert eineMROund eineMROKlasse inC , super()gibt ein Objekt zurück, das Methoden ausMROMediumCden Klassen danach nachschlägt.
Das heißt, die Suche ist nicht wie bei einer regulären Methode, die alleMROKlassen durchsucht, sondern vielmehr durchsucht sie das Ende vonMRO.
Zum Beispiel gibt es eineMRO:
[A, B, C, D, E, object]
der folgende Aufruf:
super(C, A).foo()
supersucht nur abCweiter, das heißt: nurDoderEoderobjectwird nach derfoo-Methode gesucht.
Wie super bei Mehrfachvererbung funktioniert
Kehren wir zum früheren zurück
d = D() d.add(2) print(d.n)
Jetzt hast du vielleicht eine Ahnung, warum die Ausgabe so ist
self is <__main__.D object at 0x10ce10e48> @D.add self is <__main__.D object at 0x10ce10e48> @B.add self is <__main__.D object at 0x10ce10e48> @C.add self is <__main__.D object at 0x10ce10e48> @A.add 19
, oder? ;)
Analysieren wir es unten im Detail:
DvonMROist:[D, B, C, A, object] 。 Hinweis:Du kannstD.mro()(Python 2 verwendetD.__mro__) umDvonMRO-Informationen anzuzeigen)
-
Die detaillierte Codeanalyse lautet wie folgt:
class A: def __init__(self): self.n = 2 def add(self, m): # 第四步 # 来自 D.add 中的 super # self == d, self.n == d.n == 5 print('self is {0} @A.add'.format(self)) self.n += m # d.n == 7 class B(A): def __init__(self): self.n = 3 def add(self, m): # 第二步 # 来自 D.add 中的 super # self == d, self.n == d.n == 5 print('self is {0} @B.add'.format(self)) # 等价于 suepr(B, self).add(m) # self 的 MRO 是 [D, B, C, A, object] # 从 B 之后的 [C, A, object] 中查找 add 方法 super().add(m) # 第六步 # d.n = 11 self.n += 3 # d.n = 14 class C(A): def __init__(self): self.n = 4 def add(self, m): # 第三步 # 来自 B.add 中的 super # self == d, self.n == d.n == 5 print('self is {0} @C.add'.format(self)) # 等价于 suepr(C, self).add(m) # self 的 MRO 是 [D, B, C, A, object] # 从 C 之后的 [A, object] 中查找 add 方法 super().add(m) # 第五步 # d.n = 7 self.n += 4 # d.n = 11 class D(B, C): def __init__(self): self.n = 5 def add(self, m): # 第一步 print('self is {0} @D.add'.format(self)) # 等价于 super(D, self).add(m) # self 的 MRO 是 [D, B, C, A, object] # 从 D 之后的 [B, C, A, object] 中查找 add 方法 super().add(m) # 第七步 # d.n = 14 self.n += 5 # self.n = 19 d = D() d.add(2) print(d.n)Das Diagramm des Aufrufablaufs ist wie folgt:
D.mro() == [D, B, C, A, object] d = D() d.n == 5 d.add(2) class D(B, C): class B(A): class C(A): class A: def add(self, m): def add(self, m): def add(self, m): def add(self, m): super().add(m) 1.---> super().add(m) 2.---> super().add(m) 3.---> self.n += m self.n += 5 <------6. self.n += 3 <----5. self.n += 4 <----4. <--| (14+5=19) (11+3=14) (7+4=11) (5+2=7)
Jetzt weißt du, warum d.n nach d.add(2) 19 ist, oder? ;)
Artikelquelle: https://mozillazg.com/2016/12/python-super-is-not-as-simple-as-you-thought.html