一、單繼承
子類調用父類的一個方法,可以用super():
class A(object): def pp(self): print('pp A') class B(A): def pp(self): super().pp() print("pp B") b = B() b.pp() #結果: pp A pp B
super()常用的方法是在__init__()方法中確保父類被正確的初始化了:
super(cls,inst).__init__() #cls,init 可以省略
class A(object): def __init__(self): self.x = 1 class B(A): def __init__(self): super(B,self).__init__() self.x = self.x +1 print(self.x) b = B() #結果 2
也可以直接調用父類的一個方法 :
A.__init__(self) class A(object): def __init__(self): self.x = 1 class B(A): def __init__(self): A.__init__(self) self.x = self.x +1 print(self.x) b = B() #結果 2
二、多繼承
super().xx方法可以理解為調用了父類中的方法xx,但是其實在單繼承中是這樣,而多繼承中有些區別
如下:
class A(object): def __init__(self): print("Enter A") class B(A): def __init__(self): print('Enter B') super(B,self).__init__() print('Leave B') class C(A): def __init__(self): print('Enter C') super(C, self).__init__() print('Leave C') class D(B,C): def __init__(self): print('Enter D') super(D,self).__init__() print("Leave D") d = D() 結果為: Enter D Enter B Enter C Enter A Leave C Leave B Leave D
如果按照原本的理解,是調用了B,C類中的方法,結果不會是這樣,那么為什么會這樣呢?
其實是我們理解錯了super()
super()的用法:
1.super()的本質
先說說python中如何實現繼承---------對於你定義的每一個類,Python會計算出一個所謂的方法解析順序(MRO)列表。 這個MRO列表就是一個簡單的所有基類的線性順序表。為了實現繼承,Python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類為止。
而這個MRO列表的構造是通過一個C3線性化算法來實現的。 我們不去深究這個算法的數學原理,它實際上就是合並所有父類的MRO列表並遵循如下三條准則:
子類會先於父類被檢查
多個父類會根據它們在列表中的順序被檢查
如果對下一個類存在兩個合法的選擇,選擇第一個父類
雖然名義上來說super是用來調用父類中的方法,但是super實際上是在MRO表中找到下一個匹配的類。super原型如下:
def super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1]
兩個參數 cls 和 inst 分別做了兩件事:
1. inst 負責生成 MRO 的 list
2. 通過 cls 定位當前 MRO 中的 index, 並返回 mro[index + 1]
但是根據我們上面說的super本質知道 super 和父類其實沒有實質關聯,我們就不難理解為什么 enter B 下一句是 enter C 而不是 enter A了(如果認為 super 代表“調用父類的方法”,會想當然的認為下一句應該是enter A)。
可以用 self.__class__.__mro__ 方法來查詢當前MRO
MRO:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
在我的理解里,一個多繼承中的ORM是固定的(只要每個類之間都有繼承關系)
上方例子的中流程:
首先是class D中,輸出“Enter D" , 然后就會調用super方法,super()方法,第一個參數是D,在MRO列表中的下標(index)為0,那么調用的下一個類就是下標為(index+1)的類,即class B,
那么進入class B,輸出"Enter B" ,再次調用super(),此時的index為1,那么調用的下一個類的index為2,即class C,輸出“Enter C” , 然后在class C中,調用super(),進入class A,輸出“Enter A”,然后回到class C ,輸出 "Leave C" , 再回到class B ,輸出“Leave B”, 然后回到class D,輸出“Leave D”。結束
當你使用 super() 函數時,Python會在MRO列表上繼續搜索下一個類。 只要每個重定義的方法統一使用 super() 並只調用它一次, 那么控制流最終會遍歷完整個MRO列表,每個方法也只會被調用一次。