僅供學(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、前端等視頻資料啦鸣。