詳析Python面向對象中的繼承
一 單繼承
類繼承作為python
的三大特性之一,在我們學習python的時候是必不可少的。使用類繼承,能夠大大減少重復代碼的編寫?,F(xiàn)來記錄下,python中關于類繼承的一些知識點。
類的繼承有單繼承,多層繼承以及多重繼承,先來看看單繼承。
1. 繼承的基本語法格式如下
#類繼承語法格式,B類繼承A類 class A(): ? ? 類屬性 ? ? 類方法 ? ? ... class B(A): ? ? 類屬性 ? ? 類方法 ? ? ...
單繼承的話一般類A是沒有繼承其他派生類的,只繼承了基類。因為在python新式類中,一個類會默認去繼承基類object的,基類object
是頂級類。
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())
如上:我們定義了一個父類Father,一個子類Son,并且子類Son繼承父類Father,它們都有自己的屬性和方法。我們可以通過打印B.__ class__.__mro __ 或者Son.mro()來查看Son類的繼承情況,如下:
>>> (<class '__main__.Son'>, <class '__main__.Father'>, <class 'object'>) [<class '__main__.Son'>, <class '__main__.Father'>, <class 'object'>]
可以看到,Son
類確實繼承了Father
類,并且繼承基類object。
3. 繼承中的屬性和方法
如果一個類繼承了另外一個類,那么這個類是可以調用其繼承類的屬性和方法的(子類可以調用父類的屬性和方法),如下
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): ? ? ? ? #調用父類屬性和方法 ? ? ? ? print(self.age1, self.name1) ? ? ? ? self.father_method() if __name__ == '__main__': ? ? A = Father() ? ? B = Son() ? ? #單繼承 ? ? # print(B.__class__.__mro__) ? ? B.fun1()
結果如下:
>>>
father_age father_name
我是父親
當子類中的屬性名方法名和父類中的屬性名方法名同名時,在該子類中會覆蓋父類的屬性名和方法名(重寫)。
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): ? ? ? ? #和父類方法同名,將以子類方法為準 ? ? ? ? print("與父類方法同名,但是我是子類") ? ? def son_fun1(self): ? ? ? ? #調用父類屬性 ? ? ? ? print("子類屬性和父類屬性同名,以子類為準:", self.name1, self.age1) if __name__ == '__main__': ? ? A = Father() ? ? B = Son() ? ? #單繼承 ? ? # print(B.__class__.__mro__) ? ? B.father_method() ? ? B.son_fun1()
輸出如下:
>>>
與父類方法同名,但是我是子類
子類屬性和父類屬性同名,以子類為準: son_name son_age
4. 初始化函數(shù)__init__()和 super
上面寫的子類和父類都是不需要傳參數(shù)的,而當我們需要給類傳參數(shù)時,往往都是要初始化的,下面來看看子類繼承父類時,參數(shù)初始化的幾種情況。
子類無新增參數(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
如上,在子類無新增參數(shù)時,無需進行__init__ 初始化,直接調用父類的對象屬性即可。因為子類Son是繼承自父類Father的,所以在調用時會首先去調父類的__init__ 進行初始化
子類有新增參數(shù):
當子類有新增參數(shù)時,該怎么初始化呢?先來看看這個對不對
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新增了一個height參數(shù),然后用__init__ 進行初始化。但是從輸出結果可以看出,height參數(shù)是正常打印的,打印name和age參數(shù)時就報錯:子類Son沒有屬性’name’,因為這個時候就不會去調用父類的__init__ 進行初始化,而是直接調用子類中的__init__ 進行初始化,這時,子類初始化就會覆蓋掉父類的__init__ 初始化,從而報錯。
正確的初始化有兩種方法,如下:
#方法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
從結果可以看出,調用父類初始化后,結果就正常輸出。這是在子類__init__
初始化的時候,調用了Father.__ init __(self, name, age)對父類對象屬性進行了初始化。
現(xiàn)在來看看另外一個初始化方法super()
初始化,不過使用super()的時候要注意python的版本,因為python2和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()
結果:
>>>
175
李四 24
上面使用的是super(Son, self).__ init __ (name, age)
來對父類對象屬性進行初始化。不過這是要區(qū)分python版本的,在python3中使用super(Son, self).__ init__(name, age)或者super().__ init__(name, age)都是可以的,但是在python2的版本中,要使用super(Son, self).__ init__(name, age)來進行初始化,而且父類必須繼承object,否則就會報錯。
二 多層繼承
上面的記錄的是單繼承,現(xiàn)在來看看多層繼承。多層繼承:子類繼承自多個父類,父類只繼承自object
頂級類。
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
繼承關系:
[<class '__main__.Son'>, <class '__main__.Father'>, <class '__main__.Mother'>, <class 'object'>]
這里有兩個父類Father,Mother,一個子類Son,子類Son繼承了類Father和Mother,繼承順序是Father在前,Mother在后。從上面輸出結果來看,子類初始化過程是先進入子類Son的 __ init __方法,其次調用super(). __init __()進行父類對象屬性初始化,最后初始化完成。
從結果可以知道上面super初始化調用的是父類Father的 __ init __方法。
。也可以看出,類Father和類Mother都有一個fun方法(同名),但在子類調用時調的是父類father的fun方法。這是為什么呢?
這里就涉及到super的繼承機制,即super會根據(jù)MRO機制,從左到右依次調用父類的屬性和方法, 當父類中有同屬性名,同方法名時,以靠左的那個父類為準。
下面我們把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):#這里變動,變換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 mother
leave mother
leave son
這是mather
[<class '__main__.Son'>, <class '__main__.Mother'>, <class '__main__.Father'>, <class 'object'>]
繼承關系:
>>>
[<class '__main__.Son'>, <class '__main__.Mother'>, <class '__main__.Father'>, <class 'object'>]
可以看出,當Mother在前時,調用的就是類Mather的初始化方法和fun方法。所以在多層繼承時,一定要注意父類的位置順序。
三 多重繼承
其實super的產生就是用來解決多重繼承問題的,什么是多重繼承呢?即,子類繼承多個父類,而父類又繼承自其它相同的類,這種又被稱為菱形繼承或者磚石繼承,
如下:
A
/ \
/ \
B C
\ /
\ /
D
先來看看如下的一個菱形繼承:
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()
輸出結果:
>>>
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__ () 來進行子類初始化的,但是從結果來看,類A的初始化方法是被執(zhí)行了兩次的,一次是類B的調用,一次是類C的調用。也可以看出子類D的初始化 __ init __ 調用了B.__ init __() 和C. __init __() ?,F(xiàn)在D類繼承的父類只有B和C,但是當代碼比較復雜,繼承的類比較多時,就得的一個一個寫,如果類B和類C也是繼承自多個類,那它們的初始化方法也得重新寫,這樣就比較繁瑣,也容易出錯。
下面來使用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())
輸出結果:
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'>]
可以看到輸出結果是不一樣的,類A的初始化只執(zhí)行了一次,而且當D類有新增父類時,初始化方法也不用動,從而避免重寫初始化方法出錯,代碼也變得整潔。
這里還是和super的繼承機制有關,上面說過當子類繼承自多個父類時,super會根據(jù)MRO機制,從左到右依次調用父類的屬性和方法,而且使用super初始化父類時,會一次性初始化所有的父類,所以上面的類A初始化方法不會被調用兩次。
所以在類的多重繼承中,一般建議使用super
來初始化父類對象屬性。
到此這篇關于詳析Python面向對象中的繼承的文章就介紹到這了,更多相關Python面向對象繼承內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Pandas數(shù)據(jù)分組統(tǒng)計的實現(xiàn)示例
對數(shù)據(jù)進行分組統(tǒng)計,主要適用DataFrame對象的groupby()函數(shù),本文就來詳細的介紹下Pandas數(shù)據(jù)分組統(tǒng)計的實現(xiàn),具有一定的參考價值,感興趣的可以了解下2023-11-11使用Python的toolz庫開始函數(shù)式編程的方法
這篇文章主要介紹了使用Python的toolz庫開始函數(shù)式編程的方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-11-11python的staticmethod與classmethod實現(xiàn)實例代碼
這篇文章主要介紹了python的staticmethod與classmethod實現(xiàn)實例代碼,分享了相關代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下2018-02-02python+Splinter實現(xiàn)12306搶票功能
這篇文章主要為大家詳細介紹了python+Splinter實現(xiàn)12306搶票功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-09-09centos 下面安裝python2.7 +pip +mysqld
這篇文章主要介紹了centos 下面安裝python2.7 +pip +mysqld,需要的朋友可以參考下2014-11-11