super函数没有那么简单-super原理剖析
開始之前,先出一道題:
1 #super函數(shù)探討 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() View Code上面的運(yùn)行結(jié)果是什么?
是下面的結(jié)果嗎?
A.__init__ B.__init__ D.__init__正確答案:
A.__init__ C.__init__ B.__init__ D.__init__有沒有疑惑?super()函數(shù)不是調(diào)用指定類的父類的方法嗎!打印了A.__init__下一句為什么是C.__init__呢?
根本原因是:
super?和父類沒有實(shí)質(zhì)性的關(guān)聯(lián)
首先,我們知道新式類采用廣度優(yōu)先算法,我們來看一下上面的繼承關(guān)系:
那么,Python是如何實(shí)現(xiàn)繼承的,繼承順序又是由誰決定的呢? 對(duì)于你定義的每一個(gè)類而已,Python會(huì)計(jì)算出一個(gè)所謂的方法解析順序(MRO Method?Resolution Order)列表。類的繼承順序就是由這個(gè)MRO決定的。
MRO通過class.__mro__來查看,我們來打印一下上面例子中的MRO:
print D.__mro__ (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)注意__mro__是類的屬性,實(shí)例沒有該屬性
這個(gè)MRO列表就是一個(gè)簡(jiǎn)單的所有基類的線性順序表。為了實(shí)現(xiàn)繼承,Python會(huì)在MRO列表上從左到右開始查找基類,直到找到第一個(gè)匹配這個(gè)屬性的類為止。
而這個(gè)MRO列表的構(gòu)造是通過一個(gè)C3線性化算法來實(shí)現(xiàn)的。 它實(shí)際上就是合并所有父類的MRO列表并遵循如下三條準(zhǔn)則:
1)子類會(huì)先于父類被檢查
2)多個(gè)父類會(huì)根據(jù)它們?cè)诹斜碇械捻樞虮粰z查
3)如果對(duì)下一個(gè)類存在兩個(gè)合法的選擇,選擇第一個(gè)父類
好像還是沒明白為什么例子中,打印了A.__init__下一句為什么是C.__init__呢?
我們使用一個(gè)函數(shù)來解釋一下super的原理:
def super(cls, inst):mro = inst.__class__.mro()return mro[mro.index(cls) + 1]其中,cls 代表類,inst 代表實(shí)例,上面的代碼做了兩件事:
1)獲取 inst 的 MRO 列表
2)查找 cls 在當(dāng)前 MRO 列表中的 index, 并返回它的下一個(gè)類,即 mro[index + 1]
當(dāng)你使用?super(cls, inst)?時(shí),Python 會(huì)在 inst 的 MRO 列表上搜索 cls 的下一個(gè)類。
是不是有一種豁然開朗的趕腳!讓我們回到例子中,這里我畫出了整個(gè)流程;
從上面的流程圖就可以看出打印的順序是對(duì)的!
了解了super的原理,那么也就可以理解下面這段有趣的代碼了:
1)執(zhí)行下面代碼
1 class A(object): 2 def go(self): 3 print 'A go' 4 super(A, self).go() 5 6 a = A() 7 a.go() View Code會(huì)報(bào)錯(cuò):
AttributeError: 'super' object has no attribute 'go'2)執(zhí)行下面代碼:
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() View Code不會(huì)報(bào)錯(cuò),結(jié)果為:
A go B go充分說明了super?和父類沒有實(shí)質(zhì)性的關(guān)聯(lián)
另外,我們想出了super以外,還有一種直接調(diào)用父類方法的方法,如下:
1 #super函數(shù)探討 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() View Code為什么不用這種方法呢?我們運(yùn)行一下,看一下,結(jié)果為:
A.__init__ B.__init__ A.__init__ C.__init__ D.__init__很明顯,A的構(gòu)造函數(shù)運(yùn)行了兩次,這不是我們所希望的;所以還是用super吧!
轉(zhuǎn)載于:https://www.cnblogs.com/deeper/p/7453759.html
與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的super函数没有那么简单-super原理剖析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 线程与进程的一些应用
- 下一篇: IE盒模型和标准盒模型