[ python ] 繼承
什麼是繼承:
繼承是一種建立新類的方式,在python中,新建一個類可以繼承一個或多個父類,父類又可稱為基類和超類,新建的類稱為派生類或子類
python 中繼承分為:單繼承和多繼承
單繼承
class Father1: # 定義父類 print('class Father1') class Father2: # 定義父類 print('class Father2') class Son1(Father1): # 單繼承,父類:Father1 子類:Son1 pass class Son2(Father1, Father2): #多繼承: 父類:Father1, Father2 子類:Son2 pass
使用 __bases__ 檢視繼承:
__bases__: 檢視子類所有的父類。
print(Son1.__bases__) print(Son2.__bases__) 執行結果: (<class '__main__.Father1'>,) (<class '__main__.Father1'>, <class '__main__.Father2'>)
繼承和抽象(先抽象再繼承)
抽象:抽取類似或者說比較像的部分。
抽象分為兩個層次:
1. 講具體的物件進行分類,例如:將奧巴馬和梅西這倆物件比較像的部分抽取成類;
2. 又可以將人、豬、狗這三類比較像的部分抽取為父類(都是動物).
抽象最主要的作用是劃分類別:
繼承:是基於抽象的結果,通過程式語言去實現它,肯定是先經歷抽象這個過程,才能通過繼承的方式去表達出抽象的結構;
抽象只是分析和設計的過程中,一個動作或者說一種技巧,通過抽象可以得到類
繼承與重用性
有如下兩個類:人類和鳥類
class Person: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex class Bird: def __init__(self, name, age, kind): self.name = name self.age = age self.kind = kind
這兩個類中的屬性,有明顯的重複使用,在這種情況下,就可以為這兩個類定義一個父類,如下:
class Animal: # 抽取出來共同的屬性組成父類 def __init__(self, name, age): self.name = name self.age = age class Person(Animal): # 子類繼承父類 def __init__(self, name, age, sex): Animal.__init__(self, name, age) # 需要注意呼叫父類的方式:父類名.方法名呼叫 self.sex = sex # 子類的派生屬性 class Bird(Animal): def __init__(self, name, age, kind): Animal.__init__(self, name, age) self.kind = kind # 子類的派生屬性 p = Person('hkey', 20, 'male') b = Bird('maque', 1, 'maque') print(p.name) print(b.name) 執行結果: hkey maque
用已經有的類建立一個新的類,這樣就重用了多數類中重複的屬性和方法,不僅可以重用自己的類,也可以繼承別人的,這樣就大大縮短了軟體開發週期。
派生類
父類中沒有的屬性 在子類中出現 叫派生屬性
父類中沒有的方法 在子類中出現 叫派生方法
在繼承父類的同時,子類也可以新增自己獨有的屬性和方法,但是如果定義了和父類重複的屬性和方法,那麼呼叫的時候,就會首先呼叫子類中的屬性和方法。
super函式
python3 中 super函式的使用
子類執行父類的方法也可以直接使用super方法,如下例子幫助理解 super 的使用:
class A: def haha(self): print('A') class B(A): def haha(self): super(B, self).haha() print('B') a = A() b = B() b.haha() print('------') super(B, b).haha() 執行結果: A B ------ A
對於super函式,我們可以總結如下:
super函式只存在於python3中
super().方法名:是尋找父類的方法名並執行;
在類中方法裡可以使用super函式,在外層也可以直接使用super函式尋找父類的方法
class A: def test(self): # 父類的方法 print('A') class B(A): def test(self): # 子類的方法與父類的方法重複,當呼叫該方法時,首先呼叫子類的方法 print('B') b = B() b.test() 執行結果: B
class Animal: ''' 人和狗都是動物,所以創造一個Animal父類 ''' def __init__(self, name, aggr, hp): self.name = name self.aggr = aggr self.hp = hp def eat(self): print('%s is eating.' % self.name) class Dog(Animal): ''' 狗類繼承 Animal 類 ''' def __init__(self, name, aggr, hp, tooth): super(Dog, self).__init__(name, aggr, hp) # 使用 super 呼叫父類的方法 self.tooth = tooth # 派生屬性 def bite(self, person): '''派生方法:狗有咬人的技能''' person.hp -= self.aggr class Person(Animal): ''' 人類,繼承 Animal 類 ''' def __init__(self, name, aggr, hp, money): super(Person, self).__init__(name, aggr, hp) # 使用 super 呼叫父類的方法 self.money = money # 派生屬性 def attack(self, dog): '''派生方法:人有攻擊的技能''' dog.hp -= self.aggr p = Person('kk', 5, 100, 0) d = Dog('tt', 10, 50, 1000) print(p.hp) d.bite(p) print(p.hp) # 執行結果: # 100 # 90子類派生方法的使用
1. 通過繼承建立了派生類與基類之間的關係,它是一種‘是’的關係,比如白馬是馬,人是動物。
2. 當多個類有相同的功能,提取這些相同的功能做成基類(父類),用繼承比較好,比如教授是老師。
class Teacher: def __init__(self, name, gender): self.name = name self.gender = gender def teach(self): print('teaching.') class Professor(Teacher): pass p1 = Professor('kk', 'male') p1.teach()
多繼承
多繼承就是一個子類繼承多個父類的方式,這種方式並不多用。
class A: print('A') class B: print('B') class C(A, B): # 子類繼承兩個基類的方式,執行也是從左到右 pass C() 執行結果: A B C
繼承順序:
1. python的類可以繼承多個類,java 和 C# 中則只能繼承一個類
2. python的類如果繼承了多個類,那麼尋找父類的方式有兩種:深度優先、廣度優先。
當類是經典類, 多繼承的情況下,會按照深度優先方式查詢
當類是新式類, 多繼承的情況下,會按照廣度優先方式查詢
在python3中全都是新式類
class A: def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D, E): pass f1 = F() f1.test() print(F.mro()) # from D # [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
上面這段程式碼如下圖:
新式類繼承順序:F -> D -> B -> E -> C -> A
經典類繼承順序: F -> D -> B -> A -> E -> C
繼承原理
對於你定義的每一個類,python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的所有基類的線性順序列表。
為了實現繼承,python會在MRO列表上從左到右開始查詢基類,直到找到第一個匹配這個屬性的類為止。而這個MRO列表的構造是通過一個C3線性化演算法來實現的。我們不去深究這個演算法的數學原理,
它實際上就是合併所有父類的MRO列表並遵循如下三條準則:
1.子類會先於父類被檢查
2.多個父類會根據它們在列表中的順序被檢查
3.如果對下一個類存在兩個合法的選擇,選擇第一個父類
繼承小結
繼承的作用:
1. 減少程式碼的重用
2. 提高程式碼可讀性
3. 規範程式設計模式
幾個名詞:
抽象:抽象即抽取類似或說比較像的部分。是一個從具體到抽象的過程。
繼承:子類繼承父類的方法和屬性
派生:子類在父類方法和屬性的基礎上產生了新的方法和屬性