Python中可以直接通過調用父類名調用父類方法,在多重繼承中,使用super()是一個很好的習慣。
super的本質傳回的是MRO的下一個類
def super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1]
兩個參數 cls 和 inst 分别做了兩件事:
- inst 負責生成 MRO 的 list
- 通過 cls 定位目前 MRO 中的 index, 并傳回 mro[index + 1]
這兩件事才是 super 的實質,一定要記住!
MRO 全稱 Method Resolution Order,它代表了類繼承的順序。
很重要的一句話:在 MRO 中,基類永遠出現在派生類後面,如果有多個基類,基類的相對順序保持不變。
放幾個例子
例1:
class A:
def print(self):
print('A')
class B:
def print(self):
print('B')
class C(A):
def print(self):
print('C')
super().print()
C().print()
# C
# A
例2:
class A(object):
def __init__(self):
print('A')
class B(A):
def __init__(self):
super(B, self).__init__()
print('B')
class C(object):
def __init__(self):
print('C')
class D(C):
def __init__(self):
super(D, self).__init__()
print('D')
class E(B, D):
def __init__(self):
super(E, self).__init__()
super(A, self).__init__()
print('E')
e = E()
分析:
print(e.__class__.__mro__)得到MRO序列(<class '__main__.E'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.D'>, <class '__main__.C'>, <class 'object'>)
super(E, self).__init__()在上述MRO中找E後續的類為B,根據順序找到B類繼承類A,最後輸出A、B
super(A, self).__init__()在MRO中找A後續的類為D,根據順序找到D類繼承類C,最後輸出C、D
例3:(來自參考文獻2)
class A:
def __init__(self):
print("A", end=" ")
super().__init__()
class E:
def __init__(self):
print("E", end=" ")
super().__init__()
class B(E):
def __init__(self):
print("B", end=" ")
super().__init__()
class D:
def __init__(self):
print("D", end=" ")
super().__init__()
class C(A, B, D):
def __init__(self):
print("C", end=" ")
A.__init__(self) # A B E D
D.__init__(self) # D
B.__init__(self) # B E D
print("MRO:", [x.__name__ for x in C.__mro__])
print(C())
MRO: ['C', 'A', 'B', 'E', 'D', 'object']
C A B E D D B E D <__main__.C object at 0x1040340b8>
分析:
當執行A.__init__(self)這行代碼的時候,self指向了c執行個體對象,然後列印出A,當調用super().__init__()這行代碼的時候,實際相當于super(A, self).__init__()的調用, super(A, self)的會在self.mro中查找排在A後邊的類,這樣就實作了繼承鍊的調用了。
例4:(來自參考文獻1)
class root(object):
def __init__(self):
print('this is root')
class B(root):
def __init__(self):
print('enter B')
super().__init__()
print('leave B')
class C(root):
def __init__(self):
print('enter C')
super().__init__()
print('leave C')
class D(B, C):
pass
d = D()
print(d.__class__.__mro__)
enter B
enter C
this is root
leave C
leave B
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.root'>, <class 'object'>)
分析:
super().__init__()這句的self還是D,是以從MRO序列中一次讀取。之是以B的init會被調用是因為D中沒有定義__init__,是以會在MRO中的下一個類中找到定義的__init__。
是以,使用super,如果你要改變子類繼承的父類(由A改為B),在例子1中隻需要修改一行代碼(class C(A): -> class C(B))即可,而不需要在class C的大量代碼中去查找、修改基類名,另外一方面代碼的可移植性和重用性也更高。
參考文獻:
1. 了解 Python super(裡面提到了比較好的一篇文章)
2. python中super()的一些用法