Python super()方法学少、多繼承以及MRO順序

僅供學(xué)習(xí)剪个,轉(zhuǎn)載請注明出處

單獨(dú)調(diào)用父類的方法

需求:編寫一個(gè)類,然后再寫一個(gè)子類進(jìn)行繼承版确,使用子類去調(diào)用父類的方法1扣囊。
使用方法1打印: 胖子老板绒疗,來包檳榔侵歇。

那么先寫一個(gè)胖子老板的父類,執(zhí)行一下:

# coding=utf-8

class FatFather(object):
    def __init__(self,name):
        print('FatFather的init開始被調(diào)用')
        self.name = name
        print('FatFather的name是%s' % self.name)
        print('FatFather的init調(diào)用結(jié)束')

def main():
   ff = FatFather("胖子老板的父親")   

if __name__ == "__main__":
   main()

運(yùn)行一下這個(gè)胖子老板父類的構(gòu)造方法__init__ 如下:

[root@server81 test2]# python p2.py 
FatFather的init開始被調(diào)用
FatFather的name是胖子老板的父親
FatFather的init調(diào)用結(jié)束

好了吓蘑,那么下面來寫一個(gè)子類惕虑,也就是胖子老板類,繼承上面的類

# coding=utf-8

# 胖子老板的父類
class FatFather(object):
    def __init__(self,name):
        print('FatFather的init開始被調(diào)用')
        self.name = name
        print('調(diào)用FatFather類的name是%s' % self.name)
        print('FatFather的init調(diào)用結(jié)束')

# 胖子老板類 繼承 FatFather 類
class FatBoss(FatFather):
    def __init__(self,name,hobby):
        print('胖子老板的類被調(diào)用啦磨镶!')
        self.hobby = hobby
        FatFather.__init__(self,name)   # 直接調(diào)用父類的構(gòu)造方法
        print("%s 的愛好是 %s" % (name,self.hobby))


def main():
   #ff = FatFather("胖子老板的父親")   
   fatboss = FatBoss("胖子老板","打斗地主")

if __name__ == "__main__":
   main()

在這上面的代碼中溃蔫,我使用FatFather.__init__(self,name)直接調(diào)用父類的方法。
運(yùn)行結(jié)果如下:

[root@server81 test2]# python3 p2.py 
胖子老板的類被調(diào)用啦琳猫!
FatFather的init開始被調(diào)用
調(diào)用FatFather類的name是胖子老板
FatFather的init調(diào)用結(jié)束
胖子老板 的愛好是 打斗地主
[root@server81 test2]# 

super() 方法基本概念

那么除了直接使用 FatFather.__init__(self,name) 的方法伟叛,還可以使用super()方法來調(diào)用。

那么首先需要理解一下super() 方法的使用說明脐嫂,可以點(diǎn)擊這里訪問一下教程统刮。

描述

super() 函數(shù)是用于調(diào)用父類(超類)的一個(gè)方法。

super 是用來解決多重繼承問題的账千,直接用類名調(diào)用父類方法在使用單繼承的時(shí)候沒問題侥蒙,但是如果使用多繼承,會(huì)涉及到查找順序(MRO)蕊爵、重復(fù)調(diào)用(鉆石繼承)等種種問題辉哥。

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

語法

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

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

參數(shù)

  • type -- 類。
  • object-or-type -- 類醋旦,一般是 self

Python3.x 和 Python2.x 的一個(gè)區(qū)別是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :

Python3.x 實(shí)例:

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

Python2.x 實(shí)例:

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

使用super() 方法來改寫剛才胖子老板繼承父類的 __init__ 構(gòu)造方法

# coding=utf-8

# 胖子老板的父類
class FatFather(object):
    def __init__(self,name):
        print('FatFather的init開始被調(diào)用')
        self.name = name
        print('調(diào)用FatFather類的name是%s' % self.name)
        print('FatFather的init調(diào)用結(jié)束')

# 胖子老板類 繼承 FatFather 類
class FatBoss(FatFather):
    def __init__(self,name,hobby):
        print('胖子老板的類被調(diào)用啦植酥!')
        self.hobby = hobby
        #FatFather.__init__(self,name)   # 直接調(diào)用父類的構(gòu)造方法
        super().__init__(name)
        print("%s 的愛好是 %s" % (name,self.hobby))


def main():
   #ff = FatFather("胖子老板的父親")   
   fatboss = FatBoss("胖子老板","打斗地主")

if __name__ == "__main__":
   main()

從上面使用super方法的時(shí)候,因?yàn)槭菃卫^承修壕,直接就可以使用了腐魂。
運(yùn)行如下:

[root@server81 test2]# python3 p2.py 
胖子老板的類被調(diào)用啦!
FatFather的init開始被調(diào)用
調(diào)用FatFather類的name是胖子老板
FatFather的init調(diào)用結(jié)束
胖子老板 的愛好是 打斗地主

那么為什么說單繼承直接使用就可以呢捂人?因?yàn)?code>super()方法如果多繼承的話御雕,會(huì)涉及到一個(gè)MRO(繼承父類方法時(shí)的順序表) 的調(diào)用排序問題。

下面可以打印一下看看單繼承的MRO順序(FatBoss.__mro__)滥搭。

# coding=utf-8

# 胖子老板的父類
class FatFather(object):
    def __init__(self,name):
        print('FatFather的init開始被調(diào)用')
        self.name = name
        print('調(diào)用FatFather類的name是%s' % self.name)
        print('FatFather的init調(diào)用結(jié)束')

# 胖子老板類 繼承 FatFather 類
class FatBoss(FatFather):
    def __init__(self,name,hobby):
        print('胖子老板的類被調(diào)用啦酸纲!')
        self.hobby = hobby
        #FatFather.__init__(self,name)   # 直接調(diào)用父類的構(gòu)造方法
        super().__init__(name)
        print("%s 的愛好是 %s" % (name,self.hobby))


def main():

   print("打印FatBoss類的MRO")
   print(FatBoss.__mro__)

   print()
   print("=========== 下面按照 MRO 順序執(zhí)行super方法 =============")
   fatboss = FatBoss("胖子老板","打斗地主")


if __name__ == "__main__":
   main()

上面的代碼使用 FatBoss.__mro__ 可以打印出 FatBoss這個(gè)類經(jīng)過 python解析器的 C3算法計(jì)算過后的繼承調(diào)用順序。
運(yùn)行如下:

[root@server81 test2]# python3 p2.py 
打印FatBoss類的MRO
(<class '__main__.FatBoss'>, <class '__main__.FatFather'>, <class 'object'>)

=========== 下面按照 MRO 順序執(zhí)行super方法 =============
胖子老板的類被調(diào)用啦瑟匆!
FatFather的init開始被調(diào)用
調(diào)用FatFather類的name是胖子老板
FatFather的init調(diào)用結(jié)束
胖子老板 的愛好是 打斗地主

從上面的結(jié)果 (<class '__main__.FatBoss'>, <class '__main__.FatFather'>, <class 'object'>) 可以看出闽坡,super() 方法在 FatBoss 會(huì)直接調(diào)用父類是 FatFather ,所以單繼承是沒問題的愁溜。

那么如果多繼承的話疾嗅,會(huì)有什么問題呢?

多繼承需求

假設(shè)再寫一個(gè)胖子老板的女兒類冕象,和 胖子老板的老婆類代承,此時(shí)女兒需要同時(shí)繼承 兩個(gè)類(胖子老板類,胖子老板老婆類)渐扮。
因?yàn)榕肿永习逵幸粋€(gè)愛好论悴,胖子老板的老婆需要干活干家務(wù),那么女兒需要幫忙同時(shí)兼顧席爽。
此時(shí)女兒就是需要繼承使用這兩個(gè)父類的方法了意荤,那么該如何去寫呢?
下面來看看實(shí)現(xiàn)代碼:

# coding=utf-8

# 胖子老板的父類
class FatFather(object):
    def __init__(self,name,*args,**kwargs):
        print()
        print("=============== 開始調(diào)用 FatFather  ========================")
        print('FatFather的init開始被調(diào)用')
        self.name = name
        print('調(diào)用FatFather類的name是%s' % self.name)
        print('FatFather的init調(diào)用結(jié)束')
        print()
        print("=============== 結(jié)束調(diào)用 FatFather  ========================")

# 胖子老板類 繼承 FatFather 類
class FatBoss(FatFather):
    def __init__(self,name,hobby,*args,**kwargs):
        print()
        print("=============== 開始調(diào)用 FatBoss  ========================")
        print('胖子老板的類被調(diào)用啦只锻!')
        #super().__init__(name)
        ## 因?yàn)槎嗬^承傳遞的參數(shù)不一致玖像,所以使用不定參數(shù)
        super().__init__(name,*args,**kwargs)
        print("%s 的愛好是 %s" % (name,hobby))     
        print()
        print("=============== 結(jié)束調(diào)用 FatBoss  ========================")

# 胖子老板的老婆類 繼承 FatFather類
class FatBossWife(FatFather):
    def __init__(self,name,housework,*args,**kwargs):
        print()
        print("=============== 開始調(diào)用 FatBossWife  ========================")
        print('胖子老板的老婆類被調(diào)用啦!要學(xué)會(huì)干家務(wù)')
        #super().__init__(name)
        ## 因?yàn)槎嗬^承傳遞的參數(shù)不一致齐饮,所以使用不定參數(shù)
        super().__init__(name,*args,**kwargs)
        print("%s 需要干的家務(wù)是 %s" % (name,housework))     
        print()
        print("=============== 結(jié)束調(diào)用 FatBossWife  ========================")

# 胖子老板的女兒類 繼承 FatBoss FatBossWife類
class FatBossGril(FatBoss,FatBossWife):
    def __init__(self,name,hobby,housework):
        print('胖子老板的女兒類被調(diào)用啦捐寥!要學(xué)會(huì)干家務(wù),還要會(huì)幫胖子老板斗地主')
        super().__init__(name,hobby,housework)

def main():
   
   print("打印FatBossGril類的MRO")
   print(FatBossGril.__mro__)
 
   print()
   print("=========== 下面按照 MRO 順序執(zhí)行super方法 =============")
   gril = FatBossGril("胖子老板","打斗地主","拖地") 

if __name__ == "__main__":
   main()

運(yùn)行結(jié)果如下:

[root@server81 test2]# python3 p2.py 
打印FatBossGril類的MRO
(<class '__main__.FatBossGril'>, <class '__main__.FatBoss'>, <class '__main__.FatBossWife'>, <class '__main__.FatFather'>, <class 'object'>)

=========== 下面按照 MRO 順序執(zhí)行super方法 =============
胖子老板的女兒類被調(diào)用啦祖驱!要學(xué)會(huì)干家務(wù)握恳,還要會(huì)幫胖子老板斗地主

=============== 開始調(diào)用 FatBoss  ========================
胖子老板的類被調(diào)用啦!

=============== 開始調(diào)用 FatBossWife  ========================
胖子老板的老婆類被調(diào)用啦捺僻!要學(xué)會(huì)干家務(wù)

=============== 開始調(diào)用 FatFather  ========================
FatFather的init開始被調(diào)用
調(diào)用FatFather類的name是胖子老板
FatFather的init調(diào)用結(jié)束

=============== 結(jié)束調(diào)用 FatFather  ========================
胖子老板 需要干的家務(wù)是 拖地

=============== 結(jié)束調(diào)用 FatBossWife  ========================
胖子老板 的愛好是 打斗地主

=============== 結(jié)束調(diào)用 FatBoss  ========================
[root@server81 test2]# 

從上面的運(yùn)行結(jié)果來看乡洼,我特意給每個(gè)類的調(diào)用開始以及結(jié)束都進(jìn)行打印標(biāo)識(shí)崇裁,可以看到。
每個(gè)類開始調(diào)用是根據(jù)MRO順序進(jìn)行開始束昵,然后逐個(gè)進(jìn)行結(jié)束的拔稳。

還有就是由于因?yàn)樾枰^承不同的父類,參數(shù)不一定锹雏。
所以巴比,所有的父類都應(yīng)該加上不定參數(shù)*args , **kwargs ,不然參數(shù)不對應(yīng)是會(huì)報(bào)錯(cuò)的礁遵。

注意事項(xiàng)

  • super().__init__相對于類名.__init__轻绞,在單繼承上用法基本無差
  • 但在多繼承上有區(qū)別,super方法能保證每個(gè)父類的方法只會(huì)執(zhí)行一次佣耐,而使用類名的方法會(huì)導(dǎo)致方法被執(zhí)行多次政勃,可以嘗試寫個(gè)代碼來看輸出結(jié)果
  • 多繼承時(shí),使用super方法兼砖,對父類的傳參數(shù)稼病,應(yīng)該是由于python中super的算法導(dǎo)致的原因,必須把參數(shù)全部傳遞掖鱼,否則會(huì)報(bào)錯(cuò)
  • 單繼承時(shí),使用super方法援制,則不能全部傳遞戏挡,只能傳父類方法所需的參數(shù),否則會(huì)報(bào)錯(cuò)
  • 多繼承時(shí)晨仑,相對于使用類名.__init__方法褐墅,要把每個(gè)父類全部寫一遍, 而使用super方法,只需寫一句話便執(zhí)行了全部父類的方法洪己,這也是為何多繼承需要全部傳參的一個(gè)原因

小試牛刀(思考題)

以下的代碼的輸出將是什么? 說出你的答案并解釋妥凳。

class Parent(object):
    x = 1

class Child1(Parent):
    pass

class Child2(Parent):
    pass

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

答案, 以上代碼的輸出是:

1 1 1
1 2 1
3 2 3

使你困惑或是驚奇的是關(guān)于最后一行的輸出是 3 2 3 而不是 3 2 1。為什么改變了 Parent.x 的值還會(huì)改變 Child2.x 的值答捕,但是同時(shí) Child1.x 值卻沒有改變逝钥?

這個(gè)答案的關(guān)鍵是,在 Python 中拱镐,類變量在內(nèi)部是作為字典處理的艘款。如果一個(gè)變量的名字沒有在當(dāng)前類的字典中發(fā)現(xiàn),將搜索祖先類(比如父類)直到被引用的變量名被找到(如果這個(gè)被引用的變量名既沒有在自己所在的類又沒有在祖先類中找到沃琅,會(huì)引發(fā)一個(gè) AttributeError 異常 )哗咆。

因此,在父類中設(shè)置 x = 1 會(huì)使得類變量 x 在引用該類和其任何子類中的值為 1益眉。這就是因?yàn)榈谝粋€(gè) print 語句的輸出是 1 1 1晌柬。

隨后姥份,如果任何它的子類重寫了該值(例如,我們執(zhí)行語句 Child1.x = 2)年碘,然后澈歉,該值僅僅在子類中被改變。這就是為什么第二個(gè) print 語句的輸出是 1 2 1盛泡。

最后闷祥,如果該值在父類中被改變(例如,我們執(zhí)行語句 Parent.x = 3)傲诵,這個(gè)改變會(huì)影響到任何未重寫該值的子類當(dāng)中的值(在這個(gè)示例中被影響的子類是 Child2)凯砍。這就是為什么第三個(gè) print 輸出是 3 2 3。


關(guān)注微信公眾號(hào)拴竹,回復(fù)【資料】悟衩、Python、PHP栓拜、JAVA座泳、web,則可獲得Python幕与、PHP挑势、JAVA、前端等視頻資料啦鸣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末潮饱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子诫给,更是在濱河造成了極大的恐慌香拉,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件中狂,死亡現(xiàn)場離奇詭異凫碌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)胃榕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門盛险,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人勤晚,你說我怎么就攤上這事枉层。” “怎么了赐写?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵鸟蜡,是天一觀的道長。 經(jīng)常有香客問我挺邀,道長揉忘,這世上最難降的妖魔是什么跳座? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮泣矛,結(jié)果婚禮上疲眷,老公的妹妹穿的比我還像新娘。我一直安慰自己您朽,他們只是感情好狂丝,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哗总,像睡著了一般几颜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上讯屈,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天蛋哭,我揣著相機(jī)與錄音,去河邊找鬼涮母。 笑死谆趾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的叛本。 我是一名探鬼主播沪蓬,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼来候!你這毒婦竟也來了怜跑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤吠勘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后峡眶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剧防,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年辫樱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了峭拘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狮暑,死狀恐怖鸡挠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搬男,我是刑警寧澤拣展,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站缔逛,受9級(jí)特大地震影響备埃,放射性物質(zhì)發(fā)生泄漏姓惑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一按脚、第九天 我趴在偏房一處隱蔽的房頂上張望于毙。 院中可真熱鬧,春花似錦辅搬、人聲如沸唯沮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽介蛉。三九已至,卻和暖如春蚤氏,著一層夾襖步出監(jiān)牢的瞬間甘耿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工竿滨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留佳恬,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓于游,卻偏偏與公主長得像毁葱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子贰剥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容