首頁 > 軟體

Python中類的mro與繼承關係詳解(二)

2022-07-28 14:01:59

前言

版本:

  • windows 10.0
  • python 3.8

多重繼承

Python數位比較與類結構中有簡略提到類,且在Python中類的mro與繼承關係詳解稍有解釋繼承關係,用到一個基礎類別Animal如下:

class Animal:
    property_ = '能夠思考'

    def __init__(self, name, age, value):
        self.name_ = name
        self.age_ = age
        self.val_ = val

再定義Action活動作為另一個基礎類別:

class Action:

    def __init__(self, action, val):
        self.action_ = action
        self.val_ = val
  • 現在需定義一個Dog類,不僅是動物,還能夠跑,可以來繼承上面兩個類來定義:
class Dog(Animal, Action):

    def __init__(self, name, age, action, val):
        Animal.__init__(self, name, age, val+1)
        Action.__init__(self, action, val)

dog = Dog('大福', 8, '跑', 78)
print(dog.__dict__)
# {'name_': '大福', 'age_': 8, 'val_': 78, 'action_': '跑'}

發現列印出的範例屬性,好像字典的鍵值更新,先初始化Animal時,val傳入的值為79,而後被更新為78,這裡為什麼不能像繼承單個類一樣直接用super方法代替呢。

上一篇有提到mro解析順序,可進行嘗試,不重寫__init__方法,發現Dog類只能傳入三個引數,且都為Animal類的引數,因為繼承的兩個父類別都有該方法,優先繼承左邊的父類別方法,如果想都繼承可以考慮這樣的形式,然而會提高後續維護的困難性。

可以將最左邊的父類別改成super方式:

class Dog(Animal, Action):

    def __init__(self, name, age, action, val):
        super().__init__(name, age, val+1)
        Action.__init__(self, action, val)

mro解析順序,與上面所述一致:

Dog.mro()
# [__main__.Dog, __main__.Animal, __main__.Action, object]
  • 祖孫類

如再進行繼承,視Dog為父類別,其Animal,Action都為祖父類別,定義一個Pet類:

class Pet(Dog):
    pass
pet = Pet('大福', 8, '跑', 78)

傳入引數,和範例化的物件跟Dog一樣,如果需要改寫某個方法,可以參照之前的方法進行改寫,另外若在保留原方法的邏輯上進行補充則用super方法。

Pet類的mro:

Pet.mro()
# [__main__.Pet, __main__.Dog, __main__.Animal, __main__.Action, object]

思考片刻

通過上面的繼承及對應的mro解析順序,可以思考以下通過多重繼承類後,輸出的x屬性值為多少:

class Alpha:
    def __init__(self, val):
        self.x = val

class Beta(Alpha):
    pass

class Gamma:
    def __init__(self, val):
        self.x = val + 1
class Omega(Gamma):
    def __init__(self, val):
        super().__init__(val + 1)
class Kappa(Beta, Omega):
    pass
k = Kappa(1)
print(k.x)

如果腦內沒有一個mro解析順序圖,這裡準備了:

[__main__.Kappa, __main__.Beta, __main__.Alpha, __main__.Omega, __main__.Gamma, object]

這裡或許會有疑問,Beta後面不是Omega嗎?怎麼到Alpha了,可以先看下Omega,繼承Gamma,而GammaAlpha並不是同源的,類似於Dog類的繼承,那麼優先就會使用Alpha的__init__方法,所以在傳入引數值1的時候,僅執行了Alpha內的self.x = val,屬性x被賦值成1,在最後print輸出即為1,列印結果檢查:

print(k.x)
# 1

若把Gamma類改成繼承Alpha類,再次猜測print(k.x)的值為多少?

class Alpha:
    def __init__(self, val):
        self.x = val

class Beta(Alpha):
    pass

class Gamma(Alpha):
    def __init__(self, val):
        self.x = val + 1

class Omega(Gamma):
    def __init__(self, val):
        super().__init__(val + 1)

class Kappa(Beta, Omega):
    pass

k = Kappa(1)
print(k.x)

檢視mro解析順序:

[__main__.Kappa, __main__.Beta, __main__.Omega, __main__.Gamma, __main__.Alpha, object]

此時發現Alpha解析優先順序排在最後,BetaOmega可以看做是BetaGamma的優先順序比較,因為Omega繼承Gamma,且重寫了__init__方法,所以當傳入引數時會對Gamma類的屬性進行賦值,雖然Beta類直接繼承Alpha,但Gamma類也直接繼承,所以Alpha解析順序需要排在Gamma後面,從而當Kappa類傳入引數時,經過Omega的super加1,傳入到Gamma處時為:self.x = val + 1中的val為2,輸出的k.x的值即為3,檢視列印結果:

print(k.x)
# 3

總結

通過連續兩篇對類繼承及mro解析順序的說明,理解類在多重繼承中的變化,無論繼承多少遍,總歸要回歸本心,但也不能胡亂繼承,有條理的,有意義的繼承,才能讓自己乃至他人更好理解當下寫出的類。

到此這篇關於Python中類的mro與繼承關係詳解的文章就介紹到這了,更多相關Python mro內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com