(2022.05.18 Wed)
一個Python類可以繼承多個類传黄。繼承類過程中的方法解析順序(method resolution order址遇,即超類中有不同的同名方法時繼承的順序)可由內(nèi)置的__mro__
方法查詢。注意滋戳,類有該__mro__
屬性/方法钻蔑,而實例化的結(jié)果沒有__mro__
。比如下面這個類繼承奸鸯。
class a:
pass
class b:
pass
class c:
pass
class d(a,b):
pass
class f(c):
pass
class g(b):
pass
class k(d, f, g):
pass
>> k.__mro__
(__main__.k,
__main__.d,
__main__.a,
__main__.f,
__main__.c,
__main__.g,
__main__.b,
object)
古典類和新式類
在Python 2.x中咪笑,類的創(chuàng)建分為古典類和新式類。古典類不從object
類繼承娄涩,新式類繼承object
類窗怒。
# 這是經(jīng)典類
class ac:
pass
# 這是新式類
class ac(object):
pass
在Python 3.x中,取消了古典類,所有類的創(chuàng)建都是新式類扬虚,也就是下面三種創(chuàng)建類的方式等價
class ac:
pass
class ac(object):
pass
class ac():
pass
古典類無法使用super()
方法努隙,而新式類在調(diào)用超類時除了可以用superclassname.method()
的方式,還可以用super()
方式調(diào)用超類方法辜昵。
MRO method resolution order
有a
b
c
d
四個類荸镊,呈菱形繼承關(guān)系,即b->a
堪置,c->a
躬存,d->b,c
,其定義如下
class a:
def show(self):
print('a show')
class b(a):
pass
class c(a):
def show(self):
print('c show')
class d(b, c):
pass
類繼承順序采用DFS晋柱,即從d
類開始按照繼承類的順序做深度優(yōu)先搜索优构,。這樣就將一個復(fù)雜的繼承關(guān)系轉(zhuǎn)化為一個線性繼承關(guān)系雁竞。在Python 2.2以前的古典類中钦椭,繼承來自于MRO中第一次出現(xiàn)該超類的位置。這個案例中的類繼承順序是
碑诉。
這種繼承順序在2.2版本之后被摒棄彪腔,仍然是從左到右的順序,如果調(diào)用的方法出現(xiàn)重復(fù)類进栽,則只保留該類最后一次出現(xiàn)的位置德挣。考慮上面的案例快毛,考慮到MRO格嗅,,繼承順序是
唠帝⊥鸵矗可通過
__mro__
方法查看
>> d.__mro__
(__main__.d, __main__.b, __main__.c, __main__.a, object)
該遍歷順序相當(dāng)于從DFS結(jié)果的尾部開始查看并記錄類和超類第一次出現(xiàn)的順序,如果有重復(fù)則跳過襟衰,得到一個序列贴铜,再將該序列反轉(zhuǎn)即可得到MRO。
調(diào)用d
類的show
方法瀑晒,查看返回結(jié)果
>> d().show()
c show
之所以返回c show
是因為根據(jù)MRO的調(diào)用順序绍坝,d
在MRO序列中第一個有show
方法定義的位置,即c
類苔悦,停止轩褐,并調(diào)用該類中的show
方法。而b
中雖然繼承了show
方法但沒有顯式定義间坐,跳過灾挨。
merge/C3算法
面對更加復(fù)雜的調(diào)用順序邑退,Python 2.2起的新式類引入了merge/C3搜索算法計算MRO±统危考慮下面這個情況
class d:
pass
class e:
pass
class f:
pass
class b(d, e):
pass
class c(d, f):
pass
class a(b, c):
pass
有地技,
,根據(jù)merge/C3算法決定MRO秒拔。
定義:
-
表示
的線性繼承關(guān)系莫矗,如
,
砂缩,這里的
表示從
到
object
-
表示類
到
的序列作谚,該序列頭部元素
,尾部定義為
-
繼承的基類自左向右分別表示為
merge算法過程如下
其中merge方法的計算規(guī)則如下:在中庵芭,取
的head妹懒,如果該元素不在
的尾部序列中,將該元素從所有列表中刪除双吆,否則去
的head繼續(xù)相同的判斷眨唬。直到列表為空(結(jié)束),或沒有找到任何符合要求的頭元素(引發(fā)異常)好乐。
用merge算法計算上面繼承結(jié)構(gòu)的MRO
有
查看__mro__
方法
>> a.__mro__
(__main__.a,
__main__.b,
__main__.c,
__main__.d,
__main__.e,
__main__.f,
object)
驗證了merge算法的結(jié)果匾竿。
如果在merge算法執(zhí)行時出現(xiàn)這種情況,解釋器無法知道如何處理這種情況蔚万,直接拋出異常岭妖。
在類的多繼承中,避免出現(xiàn)菱形調(diào)用的復(fù)雜情況反璃。
Reference
1 編寫高質(zhì)量代碼 改善Python程序的91個建議昵慌,張穎等著,機(jī)械工業(yè)出版社