欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳析Python面向?qū)ο笾械睦^承

 更新時(shí)間:2022年03月07日 11:11:37   作者:搬磚,贊路費(fèi)  
這篇文章主要詳析Python面向?qū)ο笾械睦^承,類繼承作為python的三大特性之一,在我們學(xué)習(xí)python的時(shí)候是必不可少的。使用類繼承,能夠大大減少重復(fù)代碼的編寫(xiě),下文詳細(xì)內(nèi)容需要的小伙伴可以參考一下

一 單繼承

類繼承作為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)文章

最新評(píng)論