python的mro演算法(python多繼承mro)

本文目錄一覽:

Python入門精華-OOP調用父類的方法及MRO方法解析序列

在繼承關係中,我們想調用已經被覆蓋了的父類的方法,就需要如下實現:

解決方法:

要調用父類中的方法,就要使用超類(超集)方法super(),該方法旨在調用已經被覆蓋的父類的成員方法。

討論:

有關python是如何實現繼承的?

針對每一個定義的類,都會計算出一個成為方法解析順序(MRO)的元組,其只是簡單的對所有基類進行簡單地線性排列。

通過上述的C類調用MRO表,我們不難看出,它將本類開始一直到object類直接所有的父類一次性從左向右逐層向上的排列了出來(先排列自己,在排列自己的父類,最後排列父類的父類,以及最後的object)

然而MRO為何如此排列,這裡要涉及到一個非常令人討厭的數學演算法,C3線性化處理,這裡只是總結其三個約束:(簡單點說,其實就是對父類進行歸併排列)

1、先檢查子類,再檢查父類

2、有多個父類時,按照MRO表的順序依次查看

3、如果下一個待選的類出現了兩個合法的選擇,那麼就從第一個父類中選取。

4、補充一點:MRO對類的排序幾乎適用於任何定義的類層次結構。

來了來了,它真的來了:重點~~

有很多同學是否仔細看過上邊的代碼?

有關super()函數,以下重點需要各位明白:

在重寫的方法中僅使用一次super()方法時,會按照MRO表從下一個類開始搜索對應的方法或屬性,以此類推。 所以C中重寫了父類的構造,構造中有super,所以會按照順序去查找MRO中下一個類的方法,發現A中也有super,就會再去B中找對應的方法(同名方法是__init__),所以找到B的構造,可是B中又有super,就會再去MRO中B的下一個類(Base)中找對應的方法(Base的__init__()方法),所以會先列印「Base.__init__」,列印完後又因為B的__init__中還有列印「B.__init__」,所以接著列印『B.__init__』,又因為列印完後A中還有列印「A.__init__」,所以再列印「A.__init__」,最後列印「C.__init__」。這樣就可以遍歷MRO整張表中所有的對應的__init__()方法,並且讓每個方法只會被調用一次。

為了更好的記憶:當所有重寫的方法中只使用了一次super函數時,會從最上層的類依次調用其指定的方法即可以理解為(object-Base-B-A-C)。

所以,輸出結果為:

甚至於如下情況更為耐人尋味,仔細品一品:

值的一提的是:AB均沒有顯式的繼承的父類,為何結果為列印『AB』呢?這裡就要理解MRO的含義了哦!

【面向對象】Python面向對象之多繼承演算法

Python的類分為經典類和新式類:

官方推薦使用新式類替換經典類,因為經典類對於多重繼承採用的從左到右深度優先匹配演算法存在一些問題。也就是如果方法同名,有的時候會繞過一些想要訪問的方法,只指向一個方法。

2.x版本中使用的是深度優先演算法,而3.x版本使用的是c3演算法,和廣度優先演算法在某些情況下是不一樣的

以頂點為起始點,從左到右開始遍歷,當遍歷到一個節點的時候,判斷是否有左右兩個頂點,優先選擇左邊的作為頂點,再繼續遍歷下去,當遍歷完成後,選擇未曾訪問的頂點重新遍歷

如圖:根據深度優先演算法,就是v1-v2-v4-v5-v3-v6

以頂點為起始點,從左到右開始遍歷,一層一層地向下遍歷,從左到右

如圖:根據廣度優先演算法:就是v1-v2-v3-v4-v6-v5

C3 演算法:MRO是一個有序列表L,在類被創建時就計算出來。

L(Child(Base1,Base2)) = [ Child + merge( L(Base1) , L(Base2) , Base1Base2 )]

L(object) = [ object ]

L的性質:結果為列表,列表中至少有一個元素即類自己。

「+」 : 添加到列表的末尾,即 [ A + B ] = [ A,B ]

merge: ① 如果列表空則結束,非空 讀merge中第一個列表的表頭,

② 查看該表頭是否在 merge中所有列表的表尾中,或者不在表尾的第一個字母

②–③ 不在,則 放入 最終的L中,並從merge中的所有列表中刪除,然後 回到①中

②–④ 在,查看 當前列表是否是merge中的最後一個列表

④–⑤ 不是 ,跳過當前列表,讀merge中下一個列表的表頭,然後 回到 ②中

④–⑥ 是,異常。類定義失敗。

表頭: 列表的第一個元素 (列表:ABC,那麼表頭就是A,B和C就是表尾)

表尾: 列表中表頭以外的元素集合(可以為空)

merge 簡單的說即尋找合法表頭(也就是不在表尾中的表頭),如果所有表中都未找到合法表頭則異常。

例如:

L(D) = L(D(O))

= D + merge(L(O))

= D + O

= [D,O]

L(B) = L(B(D,E))

= B + merge(L(D) , L(E))

= B + merge(DO , EO) # 第一個列表DO的表頭D,其他列表比如EO的表尾都不含有D,所以可以將D提出來,即D是合法表頭

= B + D + merge(O , EO) #從第一個開始表頭是O,但是後面的列表EO的表尾中含有O所以O是不合法的,所以跳到下一個列表EO

= B + D + E + merge(O , O)

= [B,D,E,O]

同理:

L(C) = [C,E,F,O]

L(A(B,C)) = A + merge(L(B),L(C),BC)

= A + merge(BDEO,CEFO,BC)#B是合法表頭

= A + B + merge(DEO,CEFO,C)#D是合法表頭

= A + B + D + merge(EO,CEFO,C)#E不是合法表頭,跳到下一個列表CEFO,此時C是合法表頭

= A + B + D + C + merge(EO,EFO)#由於第三個列表中的C被刪除,為空,所以不存在第三個表,只剩下兩個表;此時E是合法表頭

= A + B + D + C + E + merge(O,FO)#O不是合法表頭,跳到下一個列表FO,F是合法表頭,

= A + B + D + C + E + F + merge(O,O)#O是合法表頭

= A + B + D + C + E + F + O

= [A,B,D,C,E,F,O]

L(D)

= L(D(B,C)) 取出D

= D + merge(BA+CA+BC) 查看merge的表頭,取出B,去除C,剩下A

= D + B + C + A

python的類和對象中的super函數的問題

問題一

因為在B類中調用了super方法,所以沒有執行完B類就去執行C類的程序

super方法在多重繼承程序中的調用順序,採用的是C3演算法(在python3中)。

C3演算法的規則如下

①.從底層開始,選擇入邊為零的點。

②.從左到右。

③深度探索。但受限於②規則。

每一個類都可以用mro函數查看自己的繼承順序(MRO全稱Method Resolution Order,就是用來定義繼承方法的調用順序)

對於你的程序

分析

①規則。得到D類,去掉D類以後,入邊為零的是B類和C類

②規則。選擇B類,去掉B類後,入邊為零的只有C類。結論是D–B–C–A。

在d=D()語句前加print(D.mro()),就可以列印出D類的繼承順序

[class ‘__main__.D’, class ‘__main__.B’, class ‘__main__.C’, class ‘__main__.A’, class ‘object’]

問題二

python3的繼承不同於普通的繼承,super函數已經考慮到了重複繼承的問題,所以對於A類只訪問一次

學明白了 Python 多繼承中的 MRO 演算法有什麼用

可能你有C++的基礎吧,C++確實可以不用帶上this。

我把你的代碼稍微改了一下,你可以分別看看cls1.n、cls2.n、self.n的輸出。

class cls1:

n=3

class cls2(cls1):

def __init__(self,cls1=cls1):

cls2.n += 1

self.n += 2

print ‘cls1.n:%s’%cls1.n

print ‘cls2.n:%s’%cls2.n

print ‘self.n:%s’%self.n

cls2()

print ‘cls1.n:%s’%cls1.n

可以看出兩個類和一個對象都可以在這裡調用它們的n屬性,如果只敲一個n別人讀你的代碼時就比較容易產生岐義,雖然少打了5個字元,但是不符合簡單明確的pythonic哲學。

當然你也可以就認為python語法規則就是這麼規定的。

原創文章,作者:FLGKA,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/128786.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
FLGKA的頭像FLGKA
上一篇 2024-10-03 23:25
下一篇 2024-10-03 23:25

相關推薦

  • Python計算陽曆日期對應周幾

    本文介紹如何通過Python計算任意陽曆日期對應周幾。 一、獲取日期 獲取日期可以通過Python內置的模塊datetime實現,示例代碼如下: from datetime imp…

    編程 2025-04-29
  • Python中引入上一級目錄中函數

    Python中經常需要調用其他文件夾中的模塊或函數,其中一個常見的操作是引入上一級目錄中的函數。在此,我們將從多個角度詳細解釋如何在Python中引入上一級目錄的函數。 一、加入環…

    編程 2025-04-29
  • 如何查看Anaconda中Python路徑

    對Anaconda中Python路徑即conda環境的查看進行詳細的闡述。 一、使用命令行查看 1、在Windows系統中,可以使用命令提示符(cmd)或者Anaconda Pro…

    編程 2025-04-29
  • Python列表中負數的個數

    Python列表是一個有序的集合,可以存儲多個不同類型的元素。而負數是指小於0的整數。在Python列表中,我們想要找到負數的個數,可以通過以下幾個方面進行實現。 一、使用循環遍歷…

    編程 2025-04-29
  • Python周杰倫代碼用法介紹

    本文將從多個方面對Python周杰倫代碼進行詳細的闡述。 一、代碼介紹 from urllib.request import urlopen from bs4 import Bea…

    編程 2025-04-29
  • 蝴蝶優化演算法Python版

    蝴蝶優化演算法是一種基於仿生學的優化演算法,模仿自然界中的蝴蝶進行搜索。它可以應用於多個領域的優化問題,包括數學優化、工程問題、機器學習等。本文將從多個方面對蝴蝶優化演算法Python版…

    編程 2025-04-29
  • python強行終止程序快捷鍵

    本文將從多個方面對python強行終止程序快捷鍵進行詳細闡述,並提供相應代碼示例。 一、Ctrl+C快捷鍵 Ctrl+C快捷鍵是在終端中經常用來強行終止運行的程序。當你在終端中運行…

    編程 2025-04-29
  • Python清華鏡像下載

    Python清華鏡像是一個高質量的Python開發資源鏡像站,提供了Python及其相關的開發工具、框架和文檔的下載服務。本文將從以下幾個方面對Python清華鏡像下載進行詳細的闡…

    編程 2025-04-29
  • Python程序需要編譯才能執行

    Python 被廣泛應用於數據分析、人工智慧、科學計算等領域,它的靈活性和簡單易學的性質使得越來越多的人喜歡使用 Python 進行編程。然而,在 Python 中程序執行的方式不…

    編程 2025-04-29
  • Python字典去重複工具

    使用Python語言編寫字典去重複工具,可幫助用戶快速去重複。 一、字典去重複工具的需求 在使用Python編寫程序時,我們經常需要處理數據文件,其中包含了大量的重複數據。為了方便…

    編程 2025-04-29

發表回復

登錄後才能評論