詳析Python面向?qū)ο笾械睦^承
一 單繼承
類繼承作為python
的三大特性之一,在我們學(xué)習(xí)python的時(shí)候是必不可少的。使用類繼承,能夠大大減少重復(fù)代碼的編寫(xiě)?,F(xiàn)來(lái)記錄下,python中關(guān)于類繼承的一些知識(shí)點(diǎn)。
類的繼承有單繼承,多層繼承以及多重繼承,先來(lái)看看單繼承。
1. 繼承的基本語(yǔ)法格式如下
#類繼承語(yǔ)法格式,B類繼承A類 class A(): ? ? 類屬性 ? ? 類方法 ? ? ... class B(A): ? ? 類屬性 ? ? 類方法 ? ? ...
單繼承的話一般類A是沒(méi)有繼承其他派生類的,只繼承了基類。因?yàn)樵趐ython新式類中,一個(gè)類會(huì)默認(rèn)去繼承基類object的,基類object
是頂級(jí)類。
2. 查看類繼承情況
class Father(): ? ? #這是父類 ? ? name1 = 'father_name' ? ? age1 = 'father_age' ? ? def father_method(self): ? ? ? ? print('我是父親') class Son(Father): ? ? #這是子類 ? ? name2 = 'son_name' ? ? age2 = 'son_age' ? ? def son_method(self): ? ? ? ? print('我是孩子') if __name__ == '__main__': ? ? A = Father() ? ? B = Son() ? ? #單繼承 ? ? print(B.__class__.__mro__) ? ? #或者Son.mro() ? ? print(Son.mro())
如上:我們定義了一個(gè)父類Father,一個(gè)子類Son,并且子類Son繼承父類Father,它們都有自己的屬性和方法。我們可以通過(guò)打印B.__ class__.__mro __ 或者Son.mro()來(lái)查看Son類的繼承情況,如下:
>>> (<class '__main__.Son'>, <class '__main__.Father'>, <class 'object'>) [<class '__main__.Son'>, <class '__main__.Father'>, <class 'object'>]
可以看到,Son
類確實(shí)繼承了Father
類,并且繼承基類object。
3. 繼承中的屬性和方法
如果一個(gè)類繼承了另外一個(gè)類,那么這個(gè)類是可以調(diào)用其繼承類的屬性和方法的(子類可以調(diào)用父類的屬性和方法),如下
class Father(): ? ? #這是父類 ? ? name1 = 'father_name' ? ? age1 = 'father_age' ? ? def father_method(self): ? ? ? ? print('我是父親') class Son(Father): ? ? #這是子類 ? ? name2 = 'son_name' ? ? age2 = 'son_age' ? ? def son_method(self): ? ? ? ? print('我是孩子') ? ? def fun1(self): ? ? ? ? #調(diào)用父類屬性和方法 ? ? ? ? print(self.age1, self.name1) ? ? ? ? self.father_method() if __name__ == '__main__': ? ? A = Father() ? ? B = Son() ? ? #單繼承 ? ? # print(B.__class__.__mro__) ? ? B.fun1()
結(jié)果如下:
>>>
father_age father_name
我是父親
當(dāng)子類中的屬性名方法名和父類中的屬性名方法名同名時(shí),在該子類中會(huì)覆蓋父類的屬性名和方法名(重寫(xiě))。
class Father(): ? ? #這是父類 ? ? name1 = 'father_name' ? ? age1 = 'father_age' ? ? def father_method(self): ? ? ? ? print('我是父親') class Son(Father): ? ? #這是子類 ? ? name1 = 'son_name' ? ? age1 = 'son_age' ? ? def son_method(self): ? ? ? ? print('我是孩子') ? ? def father_method(self): ? ? ? ? #和父類方法同名,將以子類方法為準(zhǔn) ? ? ? ? print("與父類方法同名,但是我是子類") ? ? def son_fun1(self): ? ? ? ? #調(diào)用父類屬性 ? ? ? ? print("子類屬性和父類屬性同名,以子類為準(zhǔn):", self.name1, self.age1) if __name__ == '__main__': ? ? A = Father() ? ? B = Son() ? ? #單繼承 ? ? # print(B.__class__.__mro__) ? ? B.father_method() ? ? B.son_fun1()
輸出如下:
>>>
與父類方法同名,但是我是子類
子類屬性和父類屬性同名,以子類為準(zhǔn): son_name son_age
4. 初始化函數(shù)__init__()和 super
上面寫(xiě)的子類和父類都是不需要傳參數(shù)的,而當(dāng)我們需要給類傳參數(shù)時(shí),往往都是要初始化的,下面來(lái)看看子類繼承父類時(shí),參數(shù)初始化的幾種情況。
子類無(wú)新增參數(shù):
class Father(): ? ? #父類 ? ? def __init__(self, name='張三', age=23): ? ? ? ? self.name = name ? ? ? ? self.age = age class Son(Father): ? ? #子類 ? ? def son_fun1(self): ? ? ? ? print(self.name, self.age) if __name__ == '__main__': ? ? B = Son() ? ? B.son_fun1()
輸出:
>>>
張三 23
如上,在子類無(wú)新增參數(shù)時(shí),無(wú)需進(jìn)行__init__ 初始化,直接調(diào)用父類的對(duì)象屬性即可。因?yàn)樽宇怱on是繼承自父類Father的,所以在調(diào)用時(shí)會(huì)首先去調(diào)父類的__init__ 進(jìn)行初始化
子類有新增參數(shù):
當(dāng)子類有新增參數(shù)時(shí),該怎么初始化呢?先來(lái)看看這個(gè)對(duì)不對(duì)
class Father(): ? ? #父類 ? ? def __init__(self, name='張三', age=23): ? ? ? ? self.name = name ? ? ? ? self.age = age class Son(Father): ? ? #子類 ? ? def __init__(self, height): ? ? ? ? self.height = height ? ? def son_fun1(self): ? ? ? ? print(self.height) ? ? ? ? print(self.name, self.age) if __name__ == '__main__': ? ? B = Son(170) ? ? B.son_fun1()
輸出:
>>>
AttributeError: 'Son' object has no attribute 'name'
170
上面子類Son新增了一個(gè)height參數(shù),然后用__init__ 進(jìn)行初始化。但是從輸出結(jié)果可以看出,height參數(shù)是正常打印的,打印name和age參數(shù)時(shí)就報(bào)錯(cuò):子類Son沒(méi)有屬性’name’,因?yàn)檫@個(gè)時(shí)候就不會(huì)去調(diào)用父類的__init__ 進(jìn)行初始化,而是直接調(diào)用子類中的__init__ 進(jìn)行初始化,這時(shí),子類初始化就會(huì)覆蓋掉父類的__init__ 初始化,從而報(bào)錯(cuò)。
正確的初始化有兩種方法,如下:
#方法1 def __init__(self, 父類參數(shù)1, 父類參數(shù)2, ..., 子類參數(shù)1, 子類參數(shù)2, ...) ?? ?父類名.__init__(self, 父類參數(shù)1, 父類參數(shù)2, ...) ?? ?self.子類屬性 = 子類屬性 class Father(): ? ? #父類 ? ? def __init__(self, name='張三', age=23): ? ? ? ? self.name = name ? ? ? ? self.age = age class Son(Father): ? ? #子類 ? ? def __init__(self, name, age, height): ? ? ? ? #方法1 ? ? ? ? Father.__init__(self, name, age) ? ? ? ? self.height = height ? ? def son_fun1(self): ? ? ? ? print(self.height) ? ? ? ? print(self.name, self.age) if __name__ == '__main__': ? ? B = Son('李四', 24, 170) ? ? B.son_fun1()
>>>
175
李四 24
從結(jié)果可以看出,調(diào)用父類初始化后,結(jié)果就正常輸出。這是在子類__init__
初始化的時(shí)候,調(diào)用了Father.__ init __(self, name, age)對(duì)父類對(duì)象屬性進(jìn)行了初始化。
現(xiàn)在來(lái)看看另外一個(gè)初始化方法super()
初始化,不過(guò)使用super()的時(shí)候要注意python的版本,因?yàn)閜ython2和python3的使用是不同的,如下
#方法2 def __init__(self, 父類參數(shù)1, 父類參數(shù)2, ..., 子類參數(shù)1, 子類參數(shù)2, ...):? ? ? #python2的super初始化 ? ? super(子類名, self).__init__(父類類參數(shù)1, 父類參數(shù)2, ...) ? ? self.子類屬性 = 子類屬性 ? ? #python3的super初始化 ? ? super().__init__(父類類參數(shù)1, 父類參數(shù)2, ...) ? ? self.子類屬性 = 子類屬性 class Father(): ? ? #父類 ? ? def __init__(self, name='張三', age=23): ? ? ? ? self.name = name ? ? ? ? self.age = age class Son(Father): ? ? #子類 ? ? def __init__(self, name, age, height): ? ? ? ? #方法2 ? ? ? ? #python2的super初始化 ? ? ? ? super(Son, self).__init__(name, age) ? ? ? ? self.height = height ? ? ? ? #python3的super初始化 ? ? ? ? # super().__init__(name, age) ? ? ? ? #或者 super(Son, self).__init__(name, age) ? ? ? ?? ? ? def son_fun1(self): ? ? ? ? print(self.height) ? ? ? ? print(self.name, self.age) if __name__ == '__main__': ? ? B = Son('李四', 24, 175) ? ? B.son_fun1()
結(jié)果:
>>>
175
李四 24
上面使用的是super(Son, self).__ init __ (name, age)
來(lái)對(duì)父類對(duì)象屬性進(jìn)行初始化。不過(guò)這是要區(qū)分python版本的,在python3中使用super(Son, self).__ init__(name, age)或者super().__ init__(name, age)都是可以的,但是在python2的版本中,要使用super(Son, self).__ init__(name, age)來(lái)進(jìn)行初始化,而且父類必須繼承object,否則就會(huì)報(bào)錯(cuò)。
二 多層繼承
上面的記錄的是單繼承,現(xiàn)在來(lái)看看多層繼承。多層繼承:子類繼承自多個(gè)父類,父類只繼承自object
頂級(jí)類。
class Father(): ? ? def __init__(self): ? ? ? ? print("enter father") ? ? ? ? print("leave father") ? ? def fun(self): ? ? ? ? print("這是father") class Mother(): ? ? def __init__(self): ? ? ? ? print("enter mother") ? ? ? ? print("leave mother") ? ? def fun(self): ? ? ? ? print("這是mather") class Son(Father, Mother): ? ? def __init__(self): ? ? ? ? print("enter son") ? ? ? ? super().__init__() ? ? ? ? print("leave son") if __name__ == '__main__': ? ? B = Son() ? ? B.fun() ? ? print(Son.mro())
輸出:
>>>
enter son
enter father
leave father
leave son
這是father
繼承關(guān)系:
[<class '__main__.Son'>, <class '__main__.Father'>, <class '__main__.Mother'>, <class 'object'>]
這里有兩個(gè)父類Father,Mother,一個(gè)子類Son,子類Son繼承了類Father和Mother,繼承順序是Father在前,Mother在后。從上面輸出結(jié)果來(lái)看,子類初始化過(guò)程是先進(jìn)入子類Son的 __ init __方法,其次調(diào)用super(). __init __()進(jìn)行父類對(duì)象屬性初始化,最后初始化完成。
從結(jié)果可以知道上面super初始化調(diào)用的是父類Father的 __ init __方法。
。也可以看出,類Father和類Mother都有一個(gè)fun方法(同名),但在子類調(diào)用時(shí)調(diào)的是父類father的fun方法。這是為什么呢?
這里就涉及到super的繼承機(jī)制,即super會(huì)根據(jù)MRO機(jī)制,從左到右依次調(diào)用父類的屬性和方法, 當(dāng)父類中有同屬性名,同方法名時(shí),以靠左的那個(gè)父類為準(zhǔn)。
下面我們把Father和Mother位置換一下,如下:
class Father(): ? ? def __init__(self): ? ? ? ? print("enter father") ? ? ? ? print("leave father") ? ? def fun(self): ? ? ? ? print("這是father") class Mother(): ? ? def __init__(self): ? ? ? ? print("enter mother") ? ? ? ? print("leave mother") ? ? def fun(self): ? ? ? ? print("這是mather") class Son(Mother, Father):#這里變動(dòng),變換Father和Mother的位置 ? ? def __init__(self): ? ? ? ? print("enter son") ? ? ? ? super().__init__() ? ? ? ? print("leave son") if __name__ == '__main__': ? ? B = Son() ? ? B.fun() ? ? print(Son.mro())
結(jié)果:
>>>
enter son
enter mother
leave mother
leave son
這是mather
[<class '__main__.Son'>, <class '__main__.Mother'>, <class '__main__.Father'>, <class 'object'>]
繼承關(guān)系:
>>>
[<class '__main__.Son'>, <class '__main__.Mother'>, <class '__main__.Father'>, <class 'object'>]
可以看出,當(dāng)Mother在前時(shí),調(diào)用的就是類Mather的初始化方法和fun方法。所以在多層繼承時(shí),一定要注意父類的位置順序。
三 多重繼承
其實(shí)super的產(chǎn)生就是用來(lái)解決多重繼承問(wèn)題的,什么是多重繼承呢?即,子類繼承多個(gè)父類,而父類又繼承自其它相同的類,這種又被稱為菱形繼承或者磚石繼承,
如下:
A
/ \
/ \
B C
\ /
\ /
D
先來(lái)看看如下的一個(gè)菱形繼承:
class A(): ? ? def __init__(self): ? ? ? ? print("enter A") ? ? ? ? print("leave A") class B(A): ? ? def __init__(self): ? ? ? ? print("enter B") ? ? ? ? # super().__init__() ? ? ? ? A.__init__(self) ? ? ? ? print("leave B") class C(A): ? ? def __init__(self): ? ? ? ? print("enter C") ? ? ? ? # super().__init__() ? ? ? ? A.__init__(self) ? ? ? ? print("leave C") class D(B, C): ? ? def __init__(self): ? ? ? ? print("enter D") ? ? ? ? B.__init__(self) ? ? ? ? C.__init__(self) ? ? ? ? # super().__init__() ? ? ? ? print("leave D") if __name__ == '__main__': ? ? d = D()
輸出結(jié)果:
>>>
enter D
enter B
enter A
leave A
leave B
enter C
enter A
leave A
leave C
leave D
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
上面用的是父類名. __ init__ () 來(lái)進(jìn)行子類初始化的,但是從結(jié)果來(lái)看,類A的初始化方法是被執(zhí)行了兩次的,一次是類B的調(diào)用,一次是類C的調(diào)用。也可以看出子類D的初始化 __ init __ 調(diào)用了B.__ init __() 和C. __init __() 。現(xiàn)在D類繼承的父類只有B和C,但是當(dāng)代碼比較復(fù)雜,繼承的類比較多時(shí),就得的一個(gè)一個(gè)寫(xiě),如果類B和類C也是繼承自多個(gè)類,那它們的初始化方法也得重新寫(xiě),這樣就比較繁瑣,也容易出錯(cuò)。
下面來(lái)使用super初始化父類看看:
class A(): ? ? def __init__(self): ? ? ? ? print("enter A") ? ? ? ? print("leave A") class B(A): ? ? def __init__(self): ? ? ? ? print("enter B") ? ? ? ? super().__init__() ? ? ? ? print("leave B") class C(A): ? ? def __init__(self): ? ? ? ? print("enter C") ? ? ? ? super().__init__() ? ? ? ? print("leave C") class D(B, C): ? ? def __init__(self): ? ? ? ? print("enter D") ? ? ? ? super().__init__() ? ? ? ? print("leave D") if __name__ == '__main__': ? ? d = D() ? ? print(D.mro())
輸出結(jié)果:
enter D
enter B
enter C
enter A
leave A
leave C
leave B
leave D
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
可以看到輸出結(jié)果是不一樣的,類A的初始化只執(zhí)行了一次,而且當(dāng)D類有新增父類時(shí),初始化方法也不用動(dòng),從而避免重寫(xiě)初始化方法出錯(cuò),代碼也變得整潔。
這里還是和super的繼承機(jī)制有關(guān),上面說(shuō)過(guò)當(dāng)子類繼承自多個(gè)父類時(shí),super會(huì)根據(jù)MRO機(jī)制,從左到右依次調(diào)用父類的屬性和方法,而且使用super初始化父類時(shí),會(huì)一次性初始化所有的父類,所以上面的類A初始化方法不會(huì)被調(diào)用兩次。
所以在類的多重繼承中,一般建議使用super
來(lái)初始化父類對(duì)象屬性。
到此這篇關(guān)于詳析Python面向?qū)ο笾械睦^承的文章就介紹到這了,更多相關(guān)Python面向?qū)ο罄^承內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python list元素為tuple時(shí)的排序方法
下面小編就為大家分享一篇python list元素為tuple時(shí)的排序方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-04-04Pandas數(shù)據(jù)分組統(tǒng)計(jì)的實(shí)現(xiàn)示例
對(duì)數(shù)據(jù)進(jìn)行分組統(tǒng)計(jì),主要適用DataFrame對(duì)象的groupby()函數(shù),本文就來(lái)詳細(xì)的介紹下Pandas數(shù)據(jù)分組統(tǒng)計(jì)的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解下2023-11-11使用Python腳本和ADB命令實(shí)現(xiàn)卸載App
這篇文章主要介紹了使用Python腳本和ADB命令實(shí)現(xiàn)卸載App的實(shí)現(xiàn)方法,文中給出了完整的示例代碼,相信對(duì)大家具有一定的參考價(jià)值,有需要的朋友們下面來(lái)一起看看吧。2017-02-02使用Python的toolz庫(kù)開(kāi)始函數(shù)式編程的方法
這篇文章主要介紹了使用Python的toolz庫(kù)開(kāi)始函數(shù)式編程的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-11-11python的staticmethod與classmethod實(shí)現(xiàn)實(shí)例代碼
這篇文章主要介紹了python的staticmethod與classmethod實(shí)現(xiàn)實(shí)例代碼,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02python+Splinter實(shí)現(xiàn)12306搶票功能
這篇文章主要為大家詳細(xì)介紹了python+Splinter實(shí)現(xiàn)12306搶票功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-09-09centos 下面安裝python2.7 +pip +mysqld
這篇文章主要介紹了centos 下面安裝python2.7 +pip +mysqld,需要的朋友可以參考下2014-11-11Python標(biāo)準(zhǔn)庫(kù)uuid模塊(生成唯一標(biāo)識(shí))詳解
uuid通過(guò)Python標(biāo)準(zhǔn)庫(kù)的uuid模塊生成通用唯一ID(或“UUID”)的一種快速簡(jiǎn)便的方法,下面這篇文章主要給大家介紹了關(guān)于Python標(biāo)準(zhǔn)庫(kù)uuid模塊(生成唯一標(biāo)識(shí))?的相關(guān)資料,需要的朋友可以參考下2022-05-05