開始以前,先出一道題:算法
1 #super函數探討 2 class A(object): 3 def __init__(self): 4 print 'A.__init__' 5 6 class B(A): 7 def __init__(self): 8 super(B, self).__init__() 9 print 'B.__init__' 10 11 class C(A): 12 def __init__(self): 13 super(C, self).__init__() 14 print 'C.__init__' 15 16 class D(B, C): 17 def __init__(self): 18 super(D, self).__init__() 19 print 'D.__init__' 20 21 d = D()
上面的運行結果是什麼?ide
是下面的結果嗎?函數
A.__init__ B.__init__ D.__init__
正確答案:spa
A.__init__ C.__init__ B.__init__ D.__init__
有沒有疑惑?super()函數不是調用指定類的父類的方法嗎!打印了A.__init__下一句爲何是C.__init__呢?code
根本緣由是:blog
super
和父類沒有實質性的關聯繼承
首先,咱們知道新式類採用廣度優先算法,咱們來看一下上面的繼承關係:it
那麼,Python是如何實現繼承的,繼承順序又是由誰決定的呢? 對於你定義的每個類而已,Python會計算出一個所謂的方法解析順序(MRO Method Resolution Order)列表。類的繼承順序就是由這個MRO決定的。io
MRO經過class.__mro__來查看,咱們來打印一下上面例子中的MRO:event
print D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
注意__mro__是類的屬性,實例沒有該屬性
這個MRO列表就是一個簡單的全部基類的線性順序表。爲了實現繼承,Python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類爲止。
而這個MRO列表的構造是經過一個C3線性化算法來實現的。 它實際上就是合併全部父類的MRO列表並遵循以下三條準則:
1)子類會先於父類被檢查
2)多個父類會根據它們在列表中的順序被檢查
3)若是對下一個類存在兩個合法的選擇,選擇第一個父類
好像仍是沒明白爲何例子中,打印了A.__init__下一句爲何是C.__init__呢?
咱們使用一個函數來解釋一下super的原理:
def super(cls, inst): mro = inst.__class__.mro() return mro[mro.index(cls) + 1]
其中,cls 表明類,inst 表明實例,上面的代碼作了兩件事:
1)獲取 inst 的 MRO 列表
2)查找 cls 在當前 MRO 列表中的 index, 並返回它的下一個類,即 mro[index + 1]
當你使用 super(cls, inst)
時,Python 會在 inst 的 MRO 列表上搜索 cls 的下一個類。
是否是有一種豁然開朗的趕腳!讓咱們回到例子中,這裏我畫出了整個流程;
從上面的流程圖就能夠看出打印的順序是對的!
瞭解了super的原理,那麼也就能夠理解下面這段有趣的代碼了:
1)執行下面代碼
1 class A(object): 2 def go(self): 3 print 'A go' 4 super(A, self).go() 5 6 a = A() 7 a.go()
會報錯:
AttributeError: 'super' object has no attribute 'go'
2)執行下面代碼:
1 class A(object): 2 def go(self): 3 print 'A go' 4 super(A, self).go() 5 6 class B(object): 7 def go(self): 8 print 'B go' 9 10 class C(A, B): 11 pass 12 13 c = C() 14 c.go()
不會報錯,結果爲:
A go
B go
充分說明了super
和父類沒有實質性的關聯
另外,咱們想出了super之外,還有一種直接調用父類方法的方法,以下:
1 #super函數探討 2 class A(object): 3 def __init__(self): 4 print 'A.__init__' 5 6 class B(A): 7 def __init__(self): 8 # super(B, self).__init__() 9 A.__init__(self) 10 print 'B.__init__' 11 12 class C(A): 13 def __init__(self): 14 # super(C, self).__init__() 15 A.__init__(self) 16 print 'C.__init__' 17 18 class D(B, C): 19 def __init__(self): 20 # super(D, self).__init__() 21 B.__init__(self) 22 C.__init__(self) 23 print 'D.__init__' 24 25 d = D()
爲何不用這種方法呢?咱們運行一下,看一下,結果爲:
A.__init__ B.__init__ A.__init__ C.__init__ D.__init__
很明顯,A的構造函數運行了兩次,這不是咱們所但願的;因此仍是用super吧!