1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 2723字,需瀏覽 6分鐘

         ·

        2020-07-13 11:21

        什么是多重繼承

        繼承是面向?qū)ο缶幊痰囊粋€(gè)重要的方式 ,通過(guò)繼承 ,子類就可以擴(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

        鉆石繼承(菱形繼承)問(wèn)題

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

        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 沒(méi)有重寫 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è)問(wèn)題是 ,比如若 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

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

        super and MRO

        其實(shí)上面兩個(gè)問(wèn)題的根源都跟 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éi)有 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ì)鉆石繼承問(wèn)題 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é)論

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

        >>>?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)論
        圖片
        表情
        推薦
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲精品自拍 | 无码视频中文字幕 | 《女教师~被淫辱の教室 | 色哟哟网| 一级黄片免费在线观看 | 亚洲狼人综合 | 亚洲成人AV在线播放 | 色婷婷AV一区二区牛牛影视 | 好好的日。com视频 | 久久久精品国产免费观看同学 |