<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          談?wù)?python 面向?qū)ο笾械亩嘀乩^承

          共 2723字,需瀏覽 6分鐘

           ·

          2020-07-13 11:21

          什么是多重繼承

          繼承是面向?qū)ο缶幊痰囊粋€(gè)重要的方式 ,通過繼承 ,子類就可以擴(kuò)展父類的功能 。和 c++ 一樣 ,在 python 中一個(gè)類能繼承自不止一個(gè)父類 ,這叫做 python 的多重繼承(Multiple Inheritance )。多重繼承的語(yǔ)法與單繼承類似 。

          class?SubclassName(BaseClass1,?BaseClass2,?BaseClass3,?...):
          ????pass

          當(dāng)然 ,子類所繼承的所有父類同樣也能有自己的父類 ,這樣就可以得到一個(gè)繼承關(guān)系機(jī)構(gòu)圖如下圖所示 :

          e83f3b4a587bb114de8ea5b3a0cc762f.webp

          鉆石繼承(菱形繼承)問題

          多重繼承容易導(dǎo)致鉆石繼承(菱形繼承)問題 ,關(guān)于為什么會(huì)叫做鉆石繼承問題 ,看下圖就知道了 :

          c6d694d23af80709da52d80952daec4a.webp


          在 python 中 ,鉆石繼承首先體現(xiàn)在父類方法的調(diào)用順序上 ,比如若B和C同時(shí)重寫了 A 中的某個(gè)方法時(shí) :


          class?A(object):
          ????def?m(self):
          ????????print("m?of?A?called")

          class?B(A):
          ????def?m(self):
          ????????print("m?of?B?called")

          class?C(A):
          ????def?m(self):
          ????????print("m?of?C?called")

          class?D(B,C):
          ????pass

          如果我們實(shí)例化 D 為 d ,然后調(diào)用 d.m() 時(shí) ,會(huì)輸出 "m of B called",如果 B 沒有重寫 A 類中的 m 方法時(shí) :

          class?A(object):
          ????def?m(self):
          ????????print("m?of?A?called")

          class?B(A):
          ????pass

          class?C(A):
          ????def?m(self):
          ????????print("m?of?C?called")

          class?D(B,C):
          ????pass

          此時(shí)調(diào)用 d.m 時(shí),則會(huì)輸出 "m of C called" , 那么如何確定父類方法的調(diào)用順序呢 ,這一切的根源還要講到方法解析順序(Method Resolution Order,MRO),這一點(diǎn)我們等會(huì)再將。

          鉆石繼承還有一個(gè)問題是 ,比如若 B 和 C 中的 m 方法也同時(shí)調(diào)用了 A 中的m方法時(shí) :

          class?A:
          ????def?m(self):
          ????????print("m?of?A?called")

          class?B(A):
          ????def?m(self):
          ????????print("m?of?B?called")
          ????????A.m(self)

          class?C(A):
          ????def?m(self):
          ????????print("m?of?C?called")
          ????????A.m(self)

          class?D(B,C):
          ????def?m(self):
          ????????print("m?of?D?called")
          ????????B.m(self)
          ????????C.m(self)

          此時(shí)我們調(diào)用 d.m ,A.m 則會(huì)執(zhí)行兩次。

          m?of?D?called
          m?of?B?called
          m?of?A?called
          m?of?C?called
          m?of?A?called

          這種問題最常見于當(dāng)我們初始化 D 類時(shí) ,那么如何才能避免鉆石繼承問題呢 ?

          super and MRO

          其實(shí)上面兩個(gè)問題的根源都跟 MRO 有關(guān) ,MRO(Method Resolution Order) 也叫方法解析順序 ,主要用于在多重繼承時(shí)判斷調(diào)的屬性來(lái)自于哪個(gè)類 ,其使用了一種叫做 C3 的算法 ,其基本思想時(shí)在避免同一類被調(diào)用多次的前提下 ,使用廣度優(yōu)先和從左到右的原則去尋找需要的屬性和方法 。當(dāng)然感興趣的同學(xué)可以移步 :MRO介紹 。

          比如針對(duì)如下的代碼 :

          >>>?class?F(object):?pass
          >>>?class?E(object):?pass
          >>>?class?D(object):?pass
          >>>?class?C(D,F):?pass
          >>>?class?B(D,E):?pass
          >>>?class?A(B,C):?pass

          當(dāng)你打印 A.__mro__ 時(shí)可以看到輸出結(jié)果為 :

          (<class?'__main__.A'>,?<class?'__main__.B'>,?<class?'__main__.C'>,?<class?'__main__.D'>,?<class?'__main__.E'>,?<class?'__main__.F'>,?<class?'object'>)

          如果我們實(shí)例化 A 為 a 并調(diào)用 a.m() 時(shí) ,如果 A 中沒有 m 方法 ,此時(shí)python 會(huì)沿著 MRO 表逐漸查找 ,直到在某個(gè)父類中找到m方法并執(zhí)行 。

          那么如何避免頂層父類中的某個(gè)方法被執(zhí)行多次呢 ,此時(shí)就需要super()來(lái)發(fā)揮作用了 ,super 本質(zhì)上是一個(gè)類 ,內(nèi)部記錄著 MRO 信息 ,由于 C3 算法確保同一個(gè)類只會(huì)被搜尋一次 ,這樣就避免了頂層父類中的方法被多次執(zhí)行了 ,比如針對(duì)鉆石繼承問題 2 中的代碼可以改為 :

          class?A(object):
          ????def?m(self):
          ????????print("m?of?A?called")

          class?B(A):
          ????def?m(self):
          ????????print("m?of?B?called")
          ????????super().m()

          class?C(A):
          ????def?m(self):
          ????????print("m?of?C?called")
          ????????super().m()

          class?D(B,C):
          ????def?m(self):
          ????????print("m?of?D?called")
          ????????super().m()

          此時(shí)打印的結(jié)果就變成了 :

          m?of?D?called
          m?of?B?called
          m?of?C?called
          m?of?A?called

          結(jié)論

          多重繼承問題是個(gè)坑 ,很多編程語(yǔ)言中并沒有多重繼承的概念 ,畢竟它帶來(lái)的麻煩比能解決的問題都要多 ,所以如果不是特別必要 ,還是盡量少用多重繼承 。如果你非要用 ,那你要好好研究下類的層次結(jié)構(gòu) ,至少要對(duì) C3 算法具有一定的了解吧 ,比如弄懂下面的代碼哪里出了問題 ?

          >>>?F=type('Food',(),{'remember2buy':'spam'})
          >>>?E=type('Eggs',(F,),{'remember2buy':'eggs'})
          >>>?G=type('GoodFood',(F,E),{})?
          #?TypeError:?Cannot?create?a?consistent?method?resolution
          #?order?(MRO)?for?bases?Food,?Eggs


          來(lái)源:小詹學(xué)Python




          _往期文章推薦_【編程課堂】計(jì)數(shù)器Counter



          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲中文无码在线观看 | 欧美成人性爱影院 | 视频黄h| 欧美视频综合网 | 激情黄色一级 |