摘要:昨天在類(lèi)的多重繼承那里糾結(jié)了好久在提問(wèn)版塊提了個(gè)問(wèn)題探討了探討鏈接才完全搞明白現(xiàn)在把類(lèi)的特性整理下供以后參考正文首先得說(shuō)明的是的類(lèi)分為經(jīng)典類(lèi)和新式類(lèi)經(jīng)典類(lèi)是之前的東西但是在還在兼容但是在之后的版本就只承認(rèn)新式類(lèi)了新式類(lèi)在之后的版本中都可以使
昨天在Python類(lèi)的多重繼承那里糾結(jié)了好久,在提問(wèn)版塊提了個(gè)問(wèn)題探討了探討(鏈接)才完全搞明白,現(xiàn)在把類(lèi)的特性整理下,供以后參考 正文
首先得說(shuō)明的是,Python的類(lèi)分為經(jīng)典類(lèi) 和 新式類(lèi)
經(jīng)典類(lèi)是python2.2之前的東西,但是在2.7還在兼容,但是在3之后的版本就只承認(rèn)新式類(lèi)了
新式類(lèi)在python2.2之后的版本中都可以使用
經(jīng)典類(lèi)是默認(rèn)沒(méi)有派生自某個(gè)基類(lèi)的,而新式類(lèi)是默認(rèn)派生自object這個(gè)基類(lèi)的:
# old style class A():pass # new style class A(obejct):pass
2.經(jīng)典類(lèi)在類(lèi)多重繼承的時(shí)候是采用從左到右深度優(yōu)先原則匹配方法的..而新式類(lèi)是采用C3算法(不同于廣度優(yōu)先)進(jìn)行匹配的
3.經(jīng)典類(lèi)是沒(méi)有__MRO__和instance.mro()調(diào)用的,而新式類(lèi)是有的.
為什么不用經(jīng)典類(lèi),要更換到新式類(lèi)因?yàn)樵诮?jīng)典類(lèi)中的多重繼承會(huì)有些問(wèn)題...可能導(dǎo)致在繼承樹(shù)中的方法查詢(xún)繞過(guò)后面的父類(lèi):
class A(): def foo1(self): print "A" class B(A): def foo2(self): pass class C(A): def foo1(self): print "C" class D(B, C): pass d = D() d.foo1()
按照經(jīng)典類(lèi)的查找順序從左到右深度優(yōu)先的規(guī)則,在訪(fǎng)問(wèn)d.foo1()的時(shí)候,D這個(gè)類(lèi)是沒(méi)有的..那么往上查找,先找到B,里面沒(méi)有,深度優(yōu)先,訪(fǎng)問(wèn)A,找到了foo1(),所以這時(shí)候調(diào)用的是A的foo1(),從而導(dǎo)致C重寫(xiě)的foo1()被繞過(guò).
所以python引入了新式類(lèi)的概念,每個(gè)基類(lèi)都繼承自object并且,他的匹配規(guī)則也從深度優(yōu)先換到了C3
C3算法是怎么做匹配的呢..在問(wèn)答版塊上面討論之后,歸結(jié)如下:
C3算法的一個(gè)核心是merge.
在merge列表中,如果第一個(gè)序列mro的第一個(gè)類(lèi)是出現(xiàn)在其它序列,并且也是第一個(gè),或者不出現(xiàn)其它序列,那么這個(gè)類(lèi)就會(huì)從這些序列中刪除,并合到訪(fǎng)問(wèn)順序列表中
比如:(引用問(wèn)題中zhuangzebo的回答@zhuangzebo)
class A(O):pass class B(O):pass class C(O):pass class D(A,B):pass class E(C,D):pass
首先需要知道 O(object)的mro(method resolution order)列表是[O,]
那么接下來(lái)是:
mro(A) = [A, O] mro(B) = [B, O] mro(C) = [C, O] mro(D) = [D] + merge(mro(A), mro(B), [A, B]) = [D] + merge([A, O], [B, O], [A, B]) = [D, A] + merge([O], [B, O], [B]) = [D, A, B] + merge([O], [O]) = [D, A, B, O] mro(E) = [E] + merge(mro(C), mro(D), [C, D]) = [E] + merge([C, O], [D, A, B, O], [C, D]) = [E, C] + merge([O], [D, A, B, O], [D]) = [E, C, D] + merge([O], [A, B, O]) = [E, C, D, A, B] + merge([O], [O]) = [E, C, D, A, B, O]
然后還有一種特殊情況:
比如:
merge(DO,CO,C) 先merge的是D
merge(DO,CO,C) 先merge的是C
意思就是.當(dāng)出現(xiàn)有 一個(gè)類(lèi)出現(xiàn)在兩個(gè)序列的頭(比如C) 這種情況和 這個(gè)類(lèi)只有在一個(gè)序列的頭(比如D) 這種情況同時(shí)出現(xiàn)的時(shí)候,按照順序方式匹配。
新式類(lèi)生成的訪(fǎng)問(wèn)序列被存儲(chǔ)在一個(gè)叫MRO的只讀列表中..
你可以使用instance.__MRO__或者instance.mro()來(lái)訪(fǎng)問(wèn)
最后匹配的時(shí)候就按照MRO序列的順序去匹配了
C3和廣度優(yōu)先的區(qū)別:舉個(gè)例子就完全明白了:
class A(object):pass class B(A):pass class C(B):pass class D(A):pass class E(D):pass class F(C, E):pass
按照廣度優(yōu)先遍歷,F的MRO序列應(yīng)該是[F,C,E,B,D,A]
但是C3是[F,E,D,C,B,A]
意思是你可以當(dāng)做C3是在一條鏈路上深度遍歷到和另外一條鏈路的交叉點(diǎn),然后去深度遍歷另外一條鏈路,最后遍歷交叉點(diǎn)
在經(jīng)典類(lèi)中,你如果要訪(fǎng)問(wèn)父類(lèi)的話(huà),是用類(lèi)名來(lái)訪(fǎng)問(wèn)的..
class A(): def __init__(self): print "A" class B(A): def __init__(self): print "B" A.__init__(self) #python不會(huì)默認(rèn)調(diào)用父類(lèi)的初始化函數(shù)的
這樣子看起來(lái)沒(méi)三問(wèn)題,但是如果類(lèi)的繼承結(jié)構(gòu)比較復(fù)雜,會(huì)導(dǎo)致代碼的可維護(hù)性很差..
所以新式類(lèi)推出了super這個(gè)東西...
class A(): def __init__(self): print "A" class B(A): def __init__(self): print "B" super(B,self).__init__()
這時(shí)候,又有一個(gè)問(wèn)題:當(dāng)類(lèi)是多重繼承的時(shí)候,super訪(fǎng)問(wèn)的是哪一個(gè)類(lèi)呢?
super實(shí)際上是通過(guò)__MRO__序列來(lái)確定訪(fǎng)問(wèn)哪一個(gè)類(lèi)的...實(shí)際上就是調(diào)用__MRO__中此類(lèi)后面的一個(gè)類(lèi)的方法.
比如序列為[F,E,D,C,B,A]那么F中的super就是E,E的就是D
class A(object): def __init__(self): print "enter A" print "leave A" class B(object): def __init__(self): print "enter B" print "leave B" class C(A): def __init__(self): print "enter C" super(C, self).__init__() print "leave C" class D(A): def __init__(self): print "enter D" super(D, self).__init__() print "leave D" class E(B, C): def __init__(self): print "enter E" B.__init__(self) C.__init__(self) print "leave E" class F(E, D): def __init__(self): print "enter F" E.__init__(self) D.__init__(self) print "leave F"
這時(shí)候打印出來(lái)是:
enter F enter E enter B leave B enter C enter D enter A leave A leave D leave C leave E enter D enter A leave A leave D leave F
可以看出來(lái)D和A的初始化函數(shù)被亂入了兩次!
按類(lèi)名訪(fǎng)問(wèn)就相當(dāng)于C語(yǔ)言之前的GOTO語(yǔ)句...亂跳,然后再用super按順序訪(fǎng)問(wèn)..就有問(wèn)題了
所以建議就是要么一直用super,要么一直用按照類(lèi)名訪(fǎng)問(wèn)
最佳實(shí)現(xiàn):避免多重繼承
super使用一致
不要混用經(jīng)典類(lèi)和新式類(lèi)
調(diào)用父類(lèi)的時(shí)候注意檢查類(lèi)層次
參考資料:《python高級(jí)編程》
http://www.cnblogs.com/lovemo1314/archive/2011/05/03/2035005.html
http://www.cnblogs.com/i2u9/archive/2013/03/19/pythonmroc3.html
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/37436.html
摘要:入門(mén),第一個(gè)這是一門(mén)很新的語(yǔ)言,年前后正式公布,算起來(lái)是比較年輕的編程語(yǔ)言了,更重要的是它是面向程序員的函數(shù)式編程語(yǔ)言,它的代碼運(yùn)行在之上。它通過(guò)編輯類(lèi)工具,帶來(lái)了先進(jìn)的編輯體驗(yàn),增強(qiáng)了語(yǔ)言服務(wù)。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺(jué)已經(jīng)到來(lái)了,總結(jié)過(guò)去的 2017,相信小伙們一定有很多收獲...
摘要:入門(mén),第一個(gè)這是一門(mén)很新的語(yǔ)言,年前后正式公布,算起來(lái)是比較年輕的編程語(yǔ)言了,更重要的是它是面向程序員的函數(shù)式編程語(yǔ)言,它的代碼運(yùn)行在之上。它通過(guò)編輯類(lèi)工具,帶來(lái)了先進(jìn)的編輯體驗(yàn),增強(qiáng)了語(yǔ)言服務(wù)。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺(jué)已經(jīng)到來(lái)了,總結(jié)過(guò)去的 2017,相信小伙們一定有很多收獲...
摘要:入門(mén),第一個(gè)這是一門(mén)很新的語(yǔ)言,年前后正式公布,算起來(lái)是比較年輕的編程語(yǔ)言了,更重要的是它是面向程序員的函數(shù)式編程語(yǔ)言,它的代碼運(yùn)行在之上。它通過(guò)編輯類(lèi)工具,帶來(lái)了先進(jìn)的編輯體驗(yàn),增強(qiáng)了語(yǔ)言服務(wù)。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺(jué)已經(jīng)到來(lái)了,總結(jié)過(guò)去的 2017,相信小伙們一定有很多收獲...
摘要:由于靜態(tài)方法不需要通過(guò)對(duì)象即可調(diào)用,所以偽變量在靜態(tài)方法中不可用。繼承一個(gè)抽象類(lèi)的時(shí)候,子類(lèi)必須定義父類(lèi)中的所有抽象方法另外,這些方法的訪(fǎng)問(wèn)控制必須和父類(lèi)中一樣。 extends對(duì)象繼承 PHP中類(lèi)不允許同時(shí)繼承多個(gè)父類(lèi),也就是extends后面只能跟一個(gè)父類(lèi)名稱(chēng),這個(gè)特性被稱(chēng)為PHP的單繼承特性 當(dāng)擴(kuò)展一個(gè)類(lèi),子類(lèi)就會(huì)繼承父類(lèi)所有公有的和受保護(hù)的方法。除非子類(lèi)覆蓋了父類(lèi)的方法,被...
閱讀 1418·2021-11-24 10:20
閱讀 3662·2021-11-24 09:38
閱讀 2304·2021-09-27 13:37
閱讀 2210·2021-09-22 15:25
閱讀 2283·2021-09-01 18:33
閱讀 3499·2019-08-30 15:55
閱讀 1793·2019-08-30 15:54
閱讀 2100·2019-08-30 12:50