Python的多重繼承問題 MRO和C3演算法

2021-09-11 02:57:51 字數 4087 閱讀 5202

有乙個sina的blog介紹的很好:python 多重繼承mro

mro即method resolution order,主要用於在多繼承時判斷調的屬性的路徑(來自於哪個類)。之前檢視了很多資料,說mro是基於深度優先搜尋演算法的。但不完全正確在python2.3之前是基於此演算法,但從python2.3起應用了新演算法:c3演算法。

c3演算法最早被提出是用於lisp的,應用在python中是為了解決原來基於深度優先搜尋演算法不滿足本地優先順序,和單調性的問題。

本地優先順序:指宣告時父類的順序,比如c(a,b),如果訪問c類物件屬性時,應該根據宣告順序,優先查詢a類,然後再查詢b類。

單調性:如果在c的解析順序中,a排在b的前面,那麼在c的所有子類裡,也必須滿足這個順序。

在python官網的the python 2.3 method resolution order中作者舉了例子,說明這一情況。

f=

type

('food',(

),)e=

type

('eggs'

,(f,),

)g=type

('goodfood'

,(f,e)

,)

根據本地優先順序在呼叫g類物件屬性時應該優先查詢f類,而在python2.3之前的演算法給出的順序是g e f o,而在心得c3演算法中通過阻止類層次不清晰的宣告來解決這一問題,以上宣告在c3演算法中就是非法的。

判斷mro要先確定乙個線性序列,然後查詢路徑由由序列中類的順序決定。所以c3演算法就是生成乙個線性序列。

如果繼承至乙個基類:

class

b(a)

這時b的mro序列為[b,a]

如果繼承至多個基類

class

b(a1,a2,a3 ...

)

這時b的mro序列

mro(b)

=[b]

+ merge(mro(a1)

, mro(a2)

, mro(a3)..

.,[a1,a2,a3]

)

merge操作就是c3演算法的核心

遍歷執行merge操作的序列,如果乙個序列的第乙個元素,在其他序列中也是第乙個元素,或不在其他序列出現,則從所有執行merge操作序列中刪除這個元素,合併到當前的mro中。

merge操作後的序列,繼續執行merge操作,直到merge操作的序列為空。

如果merge操作的序列無法為空,則說明不合法。

例子:

class

a(o)

:pass

class

b(o)

:pass

class

c(o)

:pass

class

e(a,b)

:pass

class

f(b,c)

:pass

class

g(e,f)

:pass

a、b、c都繼承至乙個基類,所以mro序列依次為[a,o]、[b,o]、[c,o]

mro(e)

=[e]

+ merge(mro(a)

, mro(b)

,[a,b])=

[e]+ merge(

[a,o]

,[b,o]

,[a,b]

)

執行merge操作的序列為[a,o]、[b,o]、[a,b]

a是序列[a,o]中的第乙個元素,在序列[b,o]中不出現,在序列[a,b]中也是第乙個元素,所以從執行merge操作的序列([a,o]、[b,o]、[a,b])中刪除a,合併到當前mro,[e]中。

mro(e)

=[e,a]

+ merge(

[o],

[b,o]

,[b]

)

mro(e)

=[e,a,b]

+ merge(

[o],

[o])

=[e,a,b,o]

同理

mro(f)

=[f]

+ merge(mro(b)

, mro(c)

,[b,c])=

[f]+ merge(

[b,o]

,[c,o]

,[b,c])=

[f,b]

+ merge(

[o],

[c,o]

,[c])=

[f,b,c]

+ merge(

[o],

[o])

=[f,b,c,o]

mro(g)

=[g]

+ merge(mro[e]

, mro[f]

,[e,f])=

[g]+ merge(

[e,a,b,o]

,[f,b,c,o]

,[e,f])=

[g,e]

+ merge(

[a,b,o]

,[f,b,c,o]

,[f])=

[g,e,a]

+ merge(

[b,o]

,[f,b,c,o]

,[f])=

[g,e,a,f]

+ merge(

[b,o]

,[b,c,o])=

[g,e,a,f,b]

+ merge(

[o],

[c,o])=

[g,e,a,f,b,c]

+ merge(

[o],

[o])

=[g,e,a,f,b,c,o]

自己實現了乙個mro演算法

from exceptions import exception

defc3_lineration

(kls):if

len(kls.__bases__)==1

:return

[kls, kls.__base__]

else

: l =

[c3_lineration(base)

for base in kls.__bases__]

[base for base in kls.__bases__]

)return

[kls]

+ merge(l)

defmerge

(args)

:if args:

for mro_list in args:

for class_type in mro_list:

for comp_list in args:

if class_type in comp_list[1:

]:break

else

: next_merge_list =

for arg in args:

if class_type in arg:

arg.remove(class_type)

if arg:

else

:return

[class_type]

+ merge(next_merge_list)

else

:raise exception

else

:return

classa(

object):

pass

classb(

object):

pass

classc(

object):

pass

class

e(a,b)

:pass

class

f(b,c)

:pass

class

g(e,f)

:pass

print c3_lineration(g)

python的多重繼承

在設計類的繼承關係時,通常,主線都是單一繼承下來的,如果需要 混入 額外的功能,通過多重繼承就可以實現,這種設計通常稱之為mixin。class animal object pass class mammal animal pass class runnable object def run sel...

python的多重繼承

python和c 一樣,支援多繼承。概念雖然容易,但是困難的工作是如果子類呼叫乙個自身沒有定義的屬性,它是按照何種順序去到父類尋找呢,尤其是眾多父類中有多個都包含該同名屬性。對經典類和新式類來說,屬性的查詢順序是不同的。現在我們分別看一下經典類和新式類兩種不同的表現 經典類 usr bin pyth...

python 多重繼承

python和c 一樣,支援多繼承。概念雖然容易,但是困難的工作是如果子類呼叫乙個自身沒有定義的屬性,它是按照何種順序去到父類尋找呢,尤其是眾多父類中有多個都包含該同名屬性。對經典類和新式類來說,屬性的查詢順序是不同的。現在我們分別看一下經典類和新式類兩種不同的表現 經典類 usr bin pyth...