1. 版本支持 / 寫法差異
在Python 2.x 中
如果你至今使用的還是 Python 2.x,那么你需要了解一下,在Python 2.x中存在著兩種類:經典類和新式類,
什么是經典類?
# 不繼承自object
class Ming:
pass
什么是新式類?
# 顯示繼承object
class Ming(object):
pass
在Python 3.x 中
如果你已經摒棄了Python 2.x,而已經完全投入了Python 3.x的懷抱,那你可能不需要關心,因為在Python3.x中所有的類都是新式類(所有類都(顯示/隱式)繼承自object),
有如下三種寫法,它們是等價的,
class Ming:
pass
class Ming():
pass
class Ming(object):
pass
2. 使用方法 / 獨特屬性
經典類無法使用super()

經典類的型別是 classobj

新式類的型別是 type,保持class與type的統一,
新式類增加了__slots__內置屬性, 可以把實體屬性的種類鎖定到__slots__規定的范圍之中.
新式類增加了__getattribute__方法,在獲取屬性時可以進入此方法,而經典類不會,
# 經典類:py2.7
class Kls01:
def __getattribute__(self, *args, **kwargs):
print("MING - 01")
# 新式類
class Kls02(object):
def __getattribute__(self, *args, **kwargs):
print("MING - 02")
kls02 = Kls02()
kls02.name
kls01 = Kls01()
kls01.name
輸出如下
MING - 02
Traceback (most recent call last):
File "F:/Python Script/Tornado/yang.py", line 13, in <module>
kls01.name
AttributeError: Kls01 instance has no attribute 'name'
3. MRO 查找演算法的演變
經典類中
經典類,MRO采用的是深度優先演算法,
為了驗證,這個繼承順序,在 Python2.x 中可以借助自帶模塊 inspect 來檢驗,
import inspect
class A:pass
class B(A):pass
class C(A):pass
class D(B, C):pass
print inspect.getmro(D)
輸出如下,可以看出,繼承順序是 D -> B -> A -> C
(<class __main__.D at 0x0000000005836A08>,
<class __main__.B at 0x0000000005836768>,
<class __main__.A at 0x00000000058368E8>,
<class __main__.C at 0x0000000005836AC8>)
非常好理解,但是在菱形繼承時,方法的呼叫會出現問題,

假設 d 是 D 的一個實體,那么執行 d.show()是呼叫 A.show() 呢 還是呼叫 C.show()呢?
在經典類中,由于是深度優先,所以是會選擇 A.show(),但是很明顯,C.show() 是 A.show() 的更具體化版本(顯示了更多的資訊),但我們的x.show() 沒有呼叫它,而是呼叫了 A.show(),這顯然是不合理的,所以這才有了后來的一步一步優化,
新式類中
為解決經典類 MRO 所存在的問題,Python 2.2 針對新式類提出了一種新的 MRO 計算方式:在定義類時就計算出該類的 MRO 并將其作為類的屬性,
Python 2.2 的新式類 MRO 計算方式和經典類 MRO 的計算方式非常相似:它仍然采用從左至右的深度優先遍歷,但是如果遍歷中出現重復的類,只保留最后一個,重新考慮上面「菱形繼承」的例子:

同樣地,我們也來驗證一下,另說明,在新式類中,除用inspect外,可以直接通過__mro__屬性獲取類的 MRO,
import inspect
class A(object):pass
class B(A):pass
class C(A):pass
class D(B, C):pass
# 或者通過 D.__mro__ 查找
print inspect.getmro(D)
輸出如下,可以看出,繼承順序變成了 D -> B -> C -> A
(<class '__main__.D'>,
<class '__main__.B'>,
<class '__main__.C'>,
<class '__main__.A'>,
<type 'object'>)
這下,菱形問題解決了,
再來看一個復雜一點的例子,

如果只依靠上面的演算法,我們來一起算下,其繼承關系是怎樣的,
- 首先進行深度遍歷:[C, A, X, object, Y, object, B, Y, object, X, object];
- 然后,只保留重復元素的最后一個:[C, A, B, Y, X, object],
同樣來驗證一下,
class X(object): pass
class Y(object): pass
class A(X, Y): pass
class B(Y, X): pass
class C(A, B): pass
print(C.__mro__)
輸出報錯,它告訴我們 X,Y 具有二義性的繼承關系(這是從Python 2.3后的 C3演算法 才有的),
Traceback (most recent call last):
File "F:/Python Script/Tornado/yang.py", line 7, in <module>
class C(A, B): pass
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution
order (MRO) for bases X, Y
具體為什么會這樣,我們來看一下,
對于 A 來說,其搜索順序為[A, X, Y, object];
對于 B,其搜索順序為 [B, Y, X, object];
對于 C,其搜索順序為[C, A, B, X, Y, object],
我們會發現,B 和 C 中 X、Y 的搜索順序是相反的!也就是說,當 B 被繼承時,它本身的行為竟然也發生了改變,這很容易導致不易察覺的錯誤,此外,即使把 C 搜索順序中 X 和 Y 互換仍然不能解決問題,這時候它又會和 A 中的搜索順序相矛盾,
對于復雜一點的繼承關系,我們在寫代碼的時候最好做到心中有數,接下來,就教教你,如何在層層復雜的繼承關系中,計算出繼承順序,
例如下面這張圖,

計算程序,會采用一種 merge演算法,它的基本思想如下:
- 檢查第一個串列的頭元素(如 L[B1] 的頭),記作 H,
- 若 H 未出現在其它串列的尾部,則將其輸出,并將其從所有串列中洗掉,然后回到步驟1;否則,取出下一個串列的頭部記作 H,繼續該步驟,
- 重復上述步驟,直至串列為慷訓者不能再找出可以輸出的元素,如果是前一種情況,則演算法結束;如果是后一種情況,說明無法構建繼承關系,Python 會拋出例外,
你可以在草稿紙上,參照上面的merge演算法,寫出如下程序
L[object] = [object]
L[D] = [D, object]
L[E] = [E, object]
L[F] = [F, object]
L[B] = [B, D, E, object]
L[C] = [C, D, F, object]
L[A] = [A] + merge(L[B], L[C], [B], [C])
= [A] + merge([B, D, E, object], [C, D, F, object], [B], [C])
= [A, B] + merge([D, E, object], [C, D, F, object], [C])
= [A, B, C] + merge([D, E, object], [D, F, object])
= [A, B, C, D] + merge([E, object], [F, object])
= [A, B, C, D, E] + merge([object], [F, object])
= [A, B, C, D, E, F] + merge([object], [object])
= [A, B, C, D, E, F, object]
當然,可以用代碼驗證類的 MRO,上面的例子可以寫作:
class D(object): pass
class E(object): pass
class F(object): pass
class B(D, E): pass
class C(D, F): pass
class A(B, C): pass
A.__mro__
輸出如下
(<class '__main__.A'>,
<class '__main__.B'>,
<class '__main__.C'>,
<class '__main__.
附錄:參考文章
- https://www.python.org/download/releases/2.3/mro/
- https://www.cnblogs.com/whatisfantasy/p/6046991.html
文末福利
本人原創的 《PyCharm 中文指南》一書前段時間一經發布,就火爆了整個 Python 圈,發布僅一天的時間,下載量就突破了 1000 ,并且在當天就在 Github 上就識訓了數百的 star,截至目前,下載量已經破萬,
這本書一共將近 200 頁,內含大量的圖解,制作之精良,值得每個 Python 工程師 人手一份,

為方便你下載,我將這本書上傳到 百度網盤上了,你可以自行獲取,
鏈接:https://pan.baidu.com/s/1-NzATHFtaTV1MQzek70iUQ
密碼:mft3
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/164757.html
標籤:Python
下一篇:141環形鏈表
