欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

什么是 MRO

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

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 的實(shí)例調(diào)用 d.who_am_i(),究竟執(zhí)行的是 A 中的 who_am_i() 還是 C 中的 who_am_i(),我想百分之九十以上的人都會(huì)不假思索地回答:肯定是 C 中的 who_am_i(),因?yàn)?C 是 D 的直接父類。然而,如果你把代碼用 Python 2 運(yùn)行一下就可以看到 d.who_am_i() 打印的是 I am A

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

這個(gè)例子充分展示了 MRO 的作用:決定基類中的函數(shù)到底應(yīng)該以什么樣的順序調(diào)用父類中的函數(shù)??梢悦鞔_地說,Python 發(fā)展到現(xiàn)在,MRO 算法已經(jīng)不是一個(gè)憑借著執(zhí)行結(jié)果就能猜出來的算法了。如果沒有深入到 MRO 算法的細(xì)節(jié),稍微復(fù)雜一點(diǎn)的繼承關(guān)系和方法調(diào)用都能徹底繞暈?zāi)恪?/p>

New-style Class vs. Old-style Class

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

在 Python 2.1 及以前,我們定義一個(gè)類的時(shí)候往往是這個(gè)樣子(我們把這種類稱為 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ù)版本為了保持向下兼容,同時(shí)提供以上兩種類定義用以區(qū)分 old-style class 和 new-style class。Python 3 則完全廢棄了 old-style class 的概念,不論你通過以上哪種方式書寫代碼,Python 3 都將明確認(rèn)為類 A 繼承自 object。這里我們只是引入 old-style 和 new-style 的概念,如果你對他們的區(qū)別感興趣,可以自行看 stackoverflow 上有關(guān)該問題的解釋。

理解 old-style class 的 MRO

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

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

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

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

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

按照上述深度遞歸的方式,函數(shù) d.who_am_i() 調(diào)用的搜索順序是 D, B, A, C, A。由于一個(gè)類不能兩次出現(xiàn),因此在搜索路徑中去除掉重復(fù)出現(xiàn)的 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

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

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

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

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

C3線性化算法

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

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

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

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

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

相關(guān)文章

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

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

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

    python3?字符串str和bytes相互轉(zhuǎn)換

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

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

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

    用python3 urllib破解有道翻譯反爬蟲機(jī)制詳解

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

    python保存字典數(shù)據(jù)到csv文件的完整代碼

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

    通過實(shí)例解析python描述符原理作用

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

    Web自動(dòng)化之Selenium常用操作方法大全

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

    解決pyCharm中 module 調(diào)用失敗的問題

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

    python通過txt文件批量安裝依賴包的實(shí)現(xiàn)步驟

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

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

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

最新評論