首頁  >  文章  >  後端開發  >  Python中的super()方法詳解

Python中的super()方法詳解

爱喝马黛茶的安东尼
爱喝马黛茶的安东尼轉載
2019-08-22 17:27:132340瀏覽

Python中的super()方法詳解

一、單獨呼叫父類的方法

需求:寫一個類,然後再寫一個子類進行繼承,使用子類別去呼叫父類別的方法1。

使用方法1列印: 胖子老闆,來包檳榔。

那麼先寫一個胖子老闆的父類,執行一下:

class FatFather(object):
    def __init__(self, name):
        print('FatFather的init开始被调用')
        self.name = name
        print('FatFather的name是%s' % self.name)
        print('FatFather的init调用结束')
def main():
    ff = FatFather("胖子老板的父亲")

運行一下這個胖子老闆父類的建構方法init 如下:

if __name__ == "__main__":
    main()
FatFather的init开始被调用
FatFather的name是胖子老板的父亲
FatFather的init调用结束

好了,那麼下面來寫一個子類,也就是胖子老闆類,繼承上面的類別。

# 胖子老板的父类
class FatFather(object):
    def __init__(self, name):
        print('FatFather的init开始被调用')
        self.name = name
        print('调用FatFather类的name是%s' % self.name)
        print('FatFather的init调用结束')
# 胖子老板类 继承 FatFather 类
class FatBoss(FatFather):
    def __init__(self, name, hobby):
        print('胖子老板的类被调用啦!')
        self.hobby = hobby
        FatFather.__init__(self, name)  # 直接调用父类的构造方法
        print("%s 的爱好是 %s" % (name, self.hobby))
def main():
    #ff = FatFather("胖子老板的父亲")
    fatboss = FatBoss("胖子老板", "打斗地主")

在這上面的程式碼中,我使用FatFather.init(self,name)直接呼叫父類別的方法。

運行結果如下:

if __name__ == "__main__":
    main()
胖子老板的类被调用啦!
FatFather的init开始被调用
调用FatFather类的name是胖子老板
FatFather的init调用结束
胖子老板 的爱好是 打斗地主

二、super() 方法基本概念

除了直接使用FatFather.init(self,name) 的方法,也可以使用super()方法來呼叫。

那麼首先需要看super()方法的描述和語法理解一下super() 方法的使用。

2.1 描述

super() 函數是用來呼叫父類別(超類別)的一個方法。

super 是用來解決多重繼承問題的,直接用類別名稱呼叫父類別方法在使用單一繼承的時候沒問題,但是如果使用多重繼承,會涉及到查找順序(MRO)、重複調用(鑽石繼承)等種種問題。

MRO 就是類別的方法解析順序表, 其實也就是繼承父類別方法時的順序表。

相關推薦:《Python影片教學

#2.2 文法

以下是super() 方法的語法:

super(type[, object-or-type])

參數

type -- 類別

object-or-type -- 類,一般是self

Python3.x 和Python2.x 的一個差異是: Python 3 可以使用直接使用super().xxx 來取代super(Class, self).xxx :

Python3.x 實例:

class A:
    pass
class B(A):
    def add(self, x):
        super().add(x)

Python2.x 實例:

class A(object):   # Python2.x 记得继承 object
    pass
class B(A):
    def add(self, x):
        super(B, self).add(x)

2.3 單一繼承使用super()

使用super() 方法來改寫剛才胖子老闆繼承父類別的init 建構方法

# 胖子老板的父类
class FatFather(object):
    def __init__(self, name):
        print('FatFather的init开始被调用')
        self.name = name
        print('调用FatFather类的name是%s' % self.name)
        print('FatFather的init调用结束')
# 胖子老板类 继承 FatFather 类
class FatBoss(FatFather):
    def __init__(self, name, hobby):
        print('胖子老板的类被调用啦!')
        self.hobby = hobby
        #FatFather.__init__(self,name)   # 直接调用父类的构造方法
        super().__init__(name)
        print("%s 的爱好是 %s" % (name, self.hobby))
def main():
    #ff = FatFather("胖子老板的父亲")
    fatboss = FatBoss("胖子老板", "打斗地主")

從上面使用super方法的時候,因為是單繼承,直接就可以使用了。

運行如下:

if __name__ == "__main__":
    main()
胖子老板的类被调用啦!
FatFather的init开始被调用
调用FatFather类的name是胖子老板
FatFather的init调用结束
胖子老板 的爱好是 打斗地主

那為什麼說單繼承直接使用就可以呢?因為super()方法如果多繼承的話,會牽涉到一個MRO(繼承父類別方法時的順序表) 的呼叫排序問題。

下面可以列印一下看看單繼承的MRO順序(FatBoss.mro)。

# 胖子老板的父类
class FatFather(object):
    def __init__(self, name):
        print('FatFather的init开始被调用')
        self.name = name
        print('调用FatFather类的name是%s' % self.name)
        print('FatFather的init调用结束')
# 胖子老板类 继承 FatFather 类
class FatBoss(FatFather):
    def __init__(self, name, hobby):
        print('胖子老板的类被调用啦!')
        self.hobby = hobby
        #FatFather.__init__(self,name)   # 直接调用父类的构造方法
        super().__init__(name)
        print("%s 的爱好是 %s" % (name, self.hobby))
def main():
    print("打印FatBoss类的MRO")
    print(FatBoss.__mro__)
    print()
    print("=========== 下面按照 MRO 顺序执行super方法 =============")
    fatboss = FatBoss("胖子老板", "打斗地主")

上面的程式碼使用 FatBoss.mro 可以列印出 FatBoss這個類別經過 python解析器的 C3演算法計算過後的繼承呼叫順序。

運行如下:

if __name__ == "__main__":
    main()
打印FatBoss类的MRO
(<class &#39;__main__.FatBoss&#39;>, <class &#39;__main__.FatFather&#39;>, <class &#39;object&#39;>)
=========== 下面按照 MRO 顺序执行super方法 =============
胖子老板的类被调用啦!
FatFather的init开始被调用
调用FatFather类的name是胖子老板
FatFather的init调用结束
胖子老板 的爱好是 打斗地主

從上面的結果(819fb8321e79eba8a7ae9f208b30d85a, 19021db2abd6a327a019e93aad66dcb4,

那麼如果多繼承的話,會有什麼問題呢?

2.4 多繼承使用super()

假設再寫一個胖子老闆的女兒類,和胖子老闆的老婆類,此時女兒需要同時繼承兩個類(胖子老闆類,胖老闆老婆類)。

因為胖子老闆有個嗜好,胖子老闆的老婆需要幹活幹家務,那麼女兒需要幫忙同時兼顧。

此時女兒就是需要繼承使用這兩個父類別的方法了,那該如何去寫呢?

下面來看看實作程式碼:

# 胖子老板的父类
class FatFather(object):
    def __init__(self, name, *args, **kwargs):
        print()
        print("=============== 开始调用 FatFather  ========================")
        print(&#39;FatFather的init开始被调用&#39;)
        self.name = name
        print(&#39;调用FatFather类的name是%s&#39; % self.name)
        print(&#39;FatFather的init调用结束&#39;)
        print()
        print("=============== 结束调用 FatFather  ========================")
# 胖子老板类 继承 FatFather 类
class FatBoss(FatFather):
    def __init__(self, name, hobby, *args, **kwargs):
        print()
        print("=============== 开始调用 FatBoss  ========================")
        print(&#39;胖子老板的类被调用啦!&#39;)
        #super().__init__(name)
        ## 因为多继承传递的参数不一致,所以使用不定参数
        super().__init__(name, *args, **kwargs)
        print("%s 的爱好是 %s" % (name, hobby))
        print()
        print("=============== 结束调用 FatBoss  ========================")
# 胖子老板的老婆类 继承 FatFather类
class FatBossWife(FatFather):
    def __init__(self, name, housework, *args, **kwargs):
        print()
        print("=============== 开始调用 FatBossWife  ========================")
        print(&#39;胖子老板的老婆类被调用啦!要学会干家务&#39;)
        #super().__init__(name)
        ## 因为多继承传递的参数不一致,所以使用不定参数
        super().__init__(name, *args, **kwargs)
        print("%s 需要干的家务是 %s" % (name, housework))
        print()
        print("=============== 结束调用 FatBossWife  ========================")
# 胖子老板的女儿类 继承 FatBoss FatBossWife类
class FatBossGril(FatBoss, FatBossWife):
    def __init__(self, name, hobby, housework):
        print(&#39;胖子老板的女儿类被调用啦!要学会干家务,还要会帮胖子老板斗地主&#39;)
        super().__init__(name, hobby, housework)
def main():
    print("打印FatBossGril类的MRO")
    print(FatBossGril.__mro__)
    print()
    print("=========== 下面按照 MRO 顺序执行super方法 =============")
    gril = FatBossGril("胖子老板", "打斗地主", "拖地")

運行結果如下:

if __name__ == "__main__":
    main()
打印FatBossGril类的MRO
(<class &#39;__main__.FatBossGril&#39;>, <class &#39;__main__.FatBoss&#39;>, <class &#39;__main__.FatBossWife&#39;>, 
<class &#39;__main__.FatFather&#39;>, <class &#39;object&#39;>)
=========== 下面按照 MRO 顺序执行super方法 =============
胖子老板的女儿类被调用啦!要学会干家务,还要会帮胖子老板斗地主
=============== 开始调用 FatBoss  ========================
胖子老板的类被调用啦!
=============== 开始调用 FatBossWife  ========================
胖子老板的老婆类被调用啦!要学会干家务
=============== 开始调用 FatFather  ========================
FatFather的init开始被调用
调用FatFather类的name是胖子老板
FatFather的init调用结束
=============== 结束调用 FatFather  ========================
胖子老板 需要干的家务是 拖地
=============== 结束调用 FatBossWife  ========================
胖子老板 的爱好是 打斗地主
=============== 结束调用 FatBoss  ========================

從上面的運行結果來看,我特意給每個類別的呼叫開始以及結束都進行列印標識,可以看到。

每個類別開始呼叫是根據MRO順序開始,然後逐一進行結束的。

還有就是因為需要繼承不同的父類,所以參數不一定。

所以,所有的父類別都應該加上不定參數*args , **kwargs ,不然參數不對應是會錯誤的。

三、注意事項

·super().init相對於類別名稱.init,在單一繼承上用法基本無差。

·但在多重繼承上有區別,super方法能保證每個父類別的方法只會執行一次,而使用類別名稱的方法會導致方法被執行多次,可以嘗試寫個程式碼   來看輸出結果。

·多繼承時,使用super方法,對父類別的傳參數,應該是由於python中super的演算法導致的原因,必須把參數全部傳遞,否則會報錯。

·單繼承時,使用super方法,則不能全部傳遞,只能傳父類別方法所需的參數,否則會報錯。

·多繼承時,相對於使用類別名稱.init方法,要把每個父類別全部寫一遍, 而使用super方法,只需寫一句話便執行了全部父類別的方法,這也是為   何多繼承需要全部傳參的一個原因。

四、練習

以下的程式碼的輸出將會是什麼?說出你的答案並解釋。

class Parent(object):
    x = 1
class Child1(Parent):
    pass
class Child2(Parent):
    pass
print(Parent.x, Child1.x, Child2.x)
1 1 1
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)
1 2 1

注意:Child1已經擁有了屬於自己的x

Parent.x = 3
print(Parent.x, Child1.x, Child2.x)
3 2 3

以上是Python中的super()方法詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:jianshu.com。如有侵權,請聯絡admin@php.cn刪除