亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C3 線性化算法與 MRO之Python中的多繼承

 更新時間:2021年10月04日 10:37:43   作者:A Coder is a Poet  
Python 中的方法解析順序(Method Resolution Order, MRO)定義了多繼承存在時 Python 解釋器查找函數解析的正確方式。這篇文章給大家介紹了Python中的多繼承,感興趣的朋友一起看看吧

Python 中的方法解析順序(Method Resolution Order, MRO)定義了多繼承存在時 Python 解釋器查找函數解析的正確方式。當 Python 版本從 2.2 發(fā)展到 2.3 再到現在的 Python 3,MRO算法也隨之發(fā)生了相應的變化。這種變化在很多時候影響了我們使用不同版本 Python 編程的過程。

什么是 MRO

MRO 全稱方法解析順序(Method Resolution Order)。它定義了 Python 中多繼承存在的情況下,解釋器查找函數解析的具體順序。什么是函數解析順序?我們首先用一個簡單的例子來說明。請仔細看下面代碼:

class A():
    def who_am_i(self):
        print("I am A")
        
class B(A):
    pass
        
class C(A):
    def who_am_i(self):
        print("I am C")

class D(B,C):
    pass
    
d = D()

如果我問在 Python 2 中使用 D 的實例調用 d.who_am_i(),究竟執(zhí)行的是 A 中的 who_am_i() 還是 C 中的 who_am_i(),我想百分之九十以上的人都會不假思索地回答:肯定是 C 中的 who_am_i(),因為 C 是 D 的直接父類。然而,如果你把代碼用 Python 2 運行一下就可以看到 d.who_am_i() 打印的是 I am A。

是不是覺得很混亂很奇怪?感到奇怪就對了?。?!

這個例子充分展示了 MRO 的作用:決定基類中的函數到底應該以什么樣的順序調用父類中的函數??梢悦鞔_地說,Python 發(fā)展到現在,MRO 算法已經不是一個憑借著執(zhí)行結果就能猜出來的算法了。如果沒有深入到 MRO 算法的細節(jié),稍微復雜一點的繼承關系和方法調用都能徹底繞暈你。

New-style Class vs. Old-style Class

在介紹不同版本的 MRO 算法之前,我們有必要簡單地回顧一下 Python 中類定義方式的發(fā)展歷史。盡管在 Python 3 中已經廢除了老式的類定義方式和 MRO 算法,但對于仍然廣泛使用的 Python 2 來說,不同的類定義方式與 MRO 算法之間具有緊密的聯系。了解這一點將幫助我們從 Python 2 向 Python 3 遷移時不會出現莫名其妙的錯誤。

在 Python 2.1 及以前,我們定義一個類的時候往往是這個樣子(我們把這種類稱為 old-style class):

class A:
    def __init__(self):
        pass

Python 2.2 引入了新的模型對象(new-style class),其建議新的類型通過如下方式定義:

class A(object):
    def __init__(self):
        pass

注意后一種定義方式顯示注明類 A 繼承自 object。Python 2.3 及后續(xù)版本為了保持向下兼容,同時提供以上兩種類定義用以區(qū)分 old-style class 和 new-style class。Python 3 則完全廢棄了 old-style class 的概念,不論你通過以上哪種方式書寫代碼,Python 3 都將明確認為類 A 繼承自 object。這里我們只是引入 old-style 和 new-style 的概念,如果你對他們的區(qū)別感興趣,可以自行看 stackoverflow 上有關該問題的解釋。

理解 old-style class 的 MRO

我們使用前文中的類繼承關系來介紹 Python 2 中針對 old-style class 的 MRO 算法。如果你在前面執(zhí)行過那段代碼,你可以看到調用 d.who_am_i() 打印的應該是 I am A。為什么 Python 2 的解釋器在確定 D 中的函數調用時要先搜索 A 而不是先搜索 D 的直接父類 C 呢?

這是由于 Python 2 對于 old-style class 使用了非常簡單的基于深度優(yōu)先遍歷的 MRO 算法(關于深度優(yōu)先遍歷,我想大家肯定都不陌生)。當一個類繼承自多個類時,Python 2 按照從左到右的順序深度遍歷類的繼承圖,從而確定類中函數的調用順序。這個過程具體如下:

  • 檢查當前的類里面是否有該函數,如果有則直接調用。
  • 檢查當前類的第一個父類里面是否有該函數,如果沒有則檢查父類的第一個父類是否有該函數,以此遞歸深度遍歷。
  • 如果沒有則回溯一層,檢查下一個父類里面是否有該函數并按照 2 中的方式遞歸。

上面的過程與標準的深度優(yōu)先遍歷只有一點細微的差別:步驟 2 總是按照繼承列表中類的先后順序來選擇分支的遍歷順序。具體來說,類 D 的繼承列表中類順序為 B, C,因此,類 D 按照先遍歷 B 分支再遍歷 C 分支的順序來確定 MRO。

我們繼續(xù)用第一個例子中的函數繼承圖來說明這個過程:

按照上述深度遞歸的方式,函數 d.who_am_i() 調用的搜索順序是 D, B, A, C, A。由于一個類不能兩次出現,因此在搜索路徑中去除掉重復出現的 A,得到最終的方法解析順序是 D, B, A, C。這樣一來你就明白了為什么 d.who_am_i() 打印的是 I am A 了。

在 Python 2 中,我們可以通過如下方式來查看 old-style class 的 MRO:

>>> import inspect
>>> inspect.getmro(D)

理解 new-style class 的 MRO

從上面的結果可以看到,使用深度優(yōu)先遍歷的查找算法并不合理。因此,Python 3 以及 Python 2 針對 new-style class 采用了新的 MRO 算法。如果你使用 Python 3 重新運行一遍上述腳本,你就可以看到函數 d.who_am_i() 的打印結果是 I am C。

>>> d.who_am_i()
I am C
>>> D.__mro__
(<class 'test.D'>, <class 'test.B'>, <class 'test.C'>, <class 'test.A'>, <class 'object'>)

新算法與基于深度遍歷的算法類似,但是不同在于新算法會對深度優(yōu)先遍歷得到的搜索路徑進行額外的檢查。其從左到右掃描得到的搜索路徑,對于每一個節(jié)點解釋器都會判斷該節(jié)點是不是好的節(jié)點。如果不是好的節(jié)點,那么將其從當前的搜索路徑中移除。

那么問題在于,什么是一個好的節(jié)點?我們說 N 是一個好的節(jié)點當且僅當搜索路徑中 N 之后的節(jié)點都不繼承自 N。我們還以上述的類繼承圖為例,按照深度優(yōu)先遍歷得到類 D 中函數的搜索路徑 D, B, A, C, A。之后 Python 解釋器從左向右檢查時發(fā)現第三個節(jié)點 A 不是一個好的節(jié)點,因為 A 之后的節(jié)點 C 繼承自 A。因此其將 A 從搜索路徑中移除,然后得到最后的調用順序 D, B, C, A。

采用上述算法,D 中的函數調用將優(yōu)先查找其直接父類 B 和 C 中的相應函數。

C3線性化算法

上一小結我們從直觀上概述了針對 new-style class 的 MRO 算法過程。事實上這個算法有一個明確的名字 C3 linearization。下面我們給出其形式化的計算過程。

上面的過程看起來好像很復雜,我們用一個例子來具體執(zhí)行一下,你就會覺得其實還是挺簡單的。假設我們有如下的一個類繼承關系:

class X():
    def who_am_i(self):
        print("I am a X")
        
class Y():
    def who_am_i(self):
        print("I am a Y")
        
class A(X, Y):
    def who_am_i(self):
        print("I am a A")
        
class B(Y, X):
     def who_am_i(self):
         print("I am a B")
         
class F(A, B):
    def who_am_i(self):
        print("I am a F")

Traceback (most recent call last):
  File "test.py", line 17, in <module>
    class F(A, B):
TypeError: Cannot create a consistent method resolution
order (MRO) for bases X, Y

參考文獻 Python Tutorial: Understanding Python MRO - Class search path The Python 2.3 Method Resolution Order C3 linearization

到此這篇關于C3 線性化算法與 MRO之Python中的多繼承的文章就介紹到這了,更多相關Python多繼承內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 對python中字典keys,values,items的使用詳解

    對python中字典keys,values,items的使用詳解

    今天小編就為大家分享一篇對python中字典keys,values,items的使用詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-02-02
  • python3?字符串str和bytes相互轉換

    python3?字符串str和bytes相互轉換

    這篇文章主要介紹了python3?字符串str和bytes相互轉換,在文件傳輸過程中,通常使用bytes格式的數據流,而代碼中通常用str類型,因此str和bytes的相互轉換就尤為重要,下文詳細介紹需要的小伙伴可以參考一下
    2022-03-03
  • Linux中安裝Python的交互式解釋器IPython的教程

    Linux中安裝Python的交互式解釋器IPython的教程

    IPython是一種基于Python的Shell,由于有了Python編程語言的支撐,而比一般的Shell更加強大.下面就來看一下Linux中安裝Python的交互式解釋器IPython的教程
    2016-06-06
  • 用python3 urllib破解有道翻譯反爬蟲機制詳解

    用python3 urllib破解有道翻譯反爬蟲機制詳解

    這篇文章主要介紹了python破解網易反爬蟲機制詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-08-08
  • python保存字典數據到csv文件的完整代碼

    python保存字典數據到csv文件的完整代碼

    在實際數據分析過程中,我們分析用Python來處理數據(海量的數據),我們都是把這個數據轉換為Python的對象的,比如最為常見的字典,下面這篇文章主要給大家介紹了關于python保存字典數據到csv的相關資料,需要的朋友可以參考下
    2022-06-06
  • 通過實例解析python描述符原理作用

    通過實例解析python描述符原理作用

    這篇文章主要介紹了通過實例解析python描述符原理作用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-01-01
  • Web自動化之Selenium常用操作方法大全

    Web自動化之Selenium常用操作方法大全

    Selenium是一種自動化測試工具,可以用于測試Web應用程序,它提供了一組用于自動化Web瀏覽器進行測試的API,下面這篇文章主要給大家介紹了關于Web自動化之Selenium常用操作方法的相關資料,需要的朋友可以參考下
    2023-06-06
  • 解決pyCharm中 module 調用失敗的問題

    解決pyCharm中 module 調用失敗的問題

    今天小編就為大家分享一篇解決pyCharm中 module 調用失敗的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-02-02
  • python通過txt文件批量安裝依賴包的實現步驟

    python通過txt文件批量安裝依賴包的實現步驟

    今天小編就為大家分享一篇python通過txt文件批量安裝依賴包的實現步驟,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-08-08
  • 基于python歷史天氣采集的分析

    基于python歷史天氣采集的分析

    今天小編就為大家分享一篇基于python歷史天氣采集的分析,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-02-02

最新評論