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