Python類-多繼承和MRO, since 2022-05-18

(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)先搜索优构,d \rightarrow b\rightarrow a\rightarrow object \rightarrow c\rightarrow a\rightarrow object。這樣就將一個復(fù)雜的繼承關(guān)系轉(zhuǎn)化為一個線性繼承關(guān)系雁竞。在Python 2.2以前的古典類中钦椭,繼承來自于MRO中第一次出現(xiàn)該超類的位置。這個案例中的類繼承順序是d \rightarrow b \rightarrow a\rightarrow c碑诉。

這種繼承順序在2.2版本之后被摒棄彪腔,仍然是從左到右的順序,如果調(diào)用的方法出現(xiàn)重復(fù)類进栽,則只保留該類最后一次出現(xiàn)的位置德挣。考慮上面的案例快毛,考慮到MRO格嗅,d \rightarrow b\rightarrow a\rightarrow object \rightarrow c\rightarrow a\rightarrow object,繼承順序是d \rightarrow b \rightarrow c\rightarrow a唠帝⊥鸵矗可通過__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

b\rightarrow d \rightarrow e地技,c\rightarrow d \rightarrow f,根據(jù)merge/C3算法決定MRO秒拔。

定義:

  • L[C]表示C的線性繼承關(guān)系莫矗,如L[object] = objectL[d] = do砂缩,這里的do表示從do object
  • C_1C_2...C_N表示類C_1C_N的序列作谚,該序列頭部元素(head)=C_1,尾部定義為(tail)=C_2...C_N
  • C繼承的基類自左向右分別表示為B_1B_2...B_N

merge算法過程如下
L[C(B_1...B_N)] = C + merge(L[B_1]...L[B_N], B_1, ..., B_N)
其中merge方法的計算規(guī)則如下:在L[B_1]...L[B_N], B_1, ...,B_N中庵芭,取L[B_1]的head妹懒,如果該元素不在L[B_2]...L[B_N], B_1, ...,B_N的尾部序列中,將該元素從所有列表中刪除双吆,否則去L[B_2]的head繼續(xù)相同的判斷眨唬。直到列表為空(結(jié)束),或沒有找到任何符合要求的頭元素(引發(fā)異常)好乐。

用merge算法計算上面繼承結(jié)構(gòu)的MRO
L[o]=o, L(d)=do, L(e)=eo, L(f)=fo

\begin{aligned} L[b] &=b+merge(L[d], L[e]) \\ & = b+merge(do, eo) \\ & = b + d + merge(o, eo)\\ & = b + d + e + merge(o, o)\\ & = b+d+e+o\\ & = bdeo \end{aligned}
\begin{aligned} L[c] &=c+merge(L[d], L[f]) \\ & = c+merge(do, fo) \\ & = c + d + merge(o, fo)\\ & = c + d + f + merge(o, o)\\ & = c+d+f+o\\ & = cdfo \end{aligned}
\begin{aligned} L[a] &=a+merge(L[b], L[c]) \\ & = a+merge(bdeo, cdfo) \\ & = a + b + merge(deo, cdfo)\\ & = a + b + c + merge(deo, dfo)\\ & = a+b+c+d+merge(eo, fo)\\ & = a+b+c+d+e+merge(o, fo)\\ & = a+b+c+d+e+f+merge(o, o)\\ & = a+b+c+d+e+f+o\\ & = abcdefo \end{aligned}
查看__mro__方法

>> a.__mro__
(__main__.a,
 __main__.b,
 __main__.c,
 __main__.d,
 __main__.e,
 __main__.f,
 object)

驗證了merge算法的結(jié)果匾竿。

如果在merge算法執(zhí)行時出現(xiàn)merge(ab, ba)這種情況,解釋器無法知道如何處理這種情況蔚万,直接拋出異常岭妖。

在類的多繼承中,避免出現(xiàn)菱形調(diào)用的復(fù)雜情況反璃。

Reference

1 編寫高質(zhì)量代碼 改善Python程序的91個建議昵慌,張穎等著,機(jī)械工業(yè)出版社

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末淮蜈,一起剝皮案震驚了整個濱河市废离,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌礁芦,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悼尾,死亡現(xiàn)場離奇詭異柿扣,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)闺魏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門未状,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人析桥,你說我怎么就攤上這事司草〖璐梗” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵埋虹,是天一觀的道長猜憎。 經(jīng)常有香客問我,道長搔课,這世上最難降的妖魔是什么胰柑? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮爬泥,結(jié)果婚禮上柬讨,老公的妹妹穿的比我還像新娘。我一直安慰自己袍啡,他們只是感情好踩官,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著境输,像睡著了一般蔗牡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上畴嘶,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天蛋逾,我揣著相機(jī)與錄音,去河邊找鬼窗悯。 笑死区匣,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蒋院。 我是一名探鬼主播亏钩,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼欺旧!你這毒婦竟也來了姑丑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤辞友,失蹤者是張志新(化名)和其女友劉穎栅哀,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體称龙,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡留拾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鲫尊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痴柔。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖疫向,靈堂內(nèi)的尸體忽然破棺而出咳蔚,到底是詐尸還是另有隱情豪嚎,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布谈火,位于F島的核電站侈询,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏堆巧。R本人自食惡果不足惜妄荔,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谍肤。 院中可真熱鬧啦租,春花似錦、人聲如沸荒揣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽系任。三九已至恳蹲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間俩滥,已是汗流浹背嘉蕾。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留霜旧,地道東北人错忱。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像挂据,于是被迫代替她去往敵國和親以清。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容