雲棲號資訊:【點擊查看更多行業資訊】
在這裏您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!
前言
繼承(英語:inheritance)是面向對象軟件技術當中的一個概念。如果一個類別B“繼承自”另一個類別A,就把這個B稱爲“A的子類”,而把A稱爲“B的父類別”也可以稱“A是B的超類”。繼承可以使得子類具有父類別的各種屬性和方法,而不需要再次編寫相同的代碼。在令子類別繼承父類別的同時,可以重新定義某些屬性,並重寫某些方法,即覆蓋父類別的原有屬性和方法,使其獲得與父類別不同的功能。另外,爲子類追加新的屬性和方法也是常見的做法。 一般靜態的面向對象編程語言,繼承屬於靜態的,意即在子類的行爲在編譯期就已經決定,無法在運行期擴展。
有些編程語言支持多重繼承,即一個子類可以同時有多個父類,比如C++編程語言;而在有些編程語言中,一個子類只能繼承自一個父類,比如Java編程語言,這時可以透過實現接口來實現與多重繼承相似的效果。
現今面向對象程序設計技巧中,繼承並非以繼承類別的“行爲”爲主,而是繼承類別的“類型”,使得組件的類型一致。另外在設計模式中提到一個守則,“多用合成,少用繼承”,此守則也是用來處理繼承無法在運行期動態擴展行爲的遺憾。
Python的繼承
Python有單繼承與多繼承。單繼承即子類繼承於一個類,多繼承即子類繼承於多個類,多繼承會比較少遇到,本章節主要講單繼承。
單繼承
在繼承中基類的構造方法(init()方法)不會被自動調用,它需要在其派生類的構造方法中親自專門調用。
在調用基類的方法時,需要加上基類的類名前綴,且需要帶上self參數變量。而在類中調用普通函數時並不需要帶上self參數
Python 總是首先查找對應類的方法,如果它不能在派生類中找到對應的方法,它纔開始到基類中逐個查找。(先在本類中查找調用的方法,找不到纔去基類中找) Python單繼承的案例:
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
def call(self):
print(self.name, '會叫')
class Cat(Animal):
def __init__(self,name,age,sex):
super(Cat, self).__init__(name,age) # 不要忘記從Animal類執行初始化
self.sex=sex
if __name__ == '__main__':
c = Cat('喵喵', 2, '男')
c.call()
# 輸出 喵喵 會叫 ,Cat繼承了父類Animal的方法
多繼承
對於多繼承,一個子類可以用多個父類的方法,這裏我們先給一個案例,再來詳解如果多個父類存在同名方法、父類中又有父類的同名方法時的執行順序 demo:
class Plant:
def __init__(self, color):
print("init Plant start")
self.color = color
print("init Plant end")
def show(self):
print("The Plant color is:", self.color)
class Fruit:
def __init__(self, color):
print("init Fruit start")
self.color = color
print("init Fruit end")
def show(self):
print("The Fruit color is:", self.color)
class Melon(Fruit):
def __init__(self, color):
print("init Melon start")
super(Melon, self).__init__(color)
self.color = color
print("init Melon end")
def show(self):
print("The Melon color is:", self.color)
class Mango(Fruit, Plant):
def __init__(self, color):
print("init Mango start")
Plant.__init__(self, color)
Fruit.__init__(self, color)
self.color = color
print("init Mango end")
def show_color(self):
print("The Mango color is:", self.color)
class Watermelon(Melon, Plant):
def __init__(self, color):
print("init Watermelon start")
Melon.__init__(self, color)
Plant.__init__(self, color)
self.color = color
print("init Watermelon end")
def show_color(self):
print("The Watermelon color is:", self.color)
if __name__ == "__main__":
mango = Mango("yellow")
mango.show()
watermelon = Watermelon("red")
watermelon.show()
我們定義了植物Plant類和水果Fruit類,然後定義瓜Melon類繼承水果類,定義一個芒果Mango類繼承Fruit和Plant類,定義一個西瓜Watermelon類繼承Melon類和Plant類。 執行結果爲:
init Mango start
init Plant start
init Plant end
init Fruit start
init Fruit end
init Mango end
The Fruit color is: yellow
init Watermelon start
init Melon start
init Fruit start
init Fruit end
init Melon end
init Plant start
init Plant end
init Watermelon end
The Melon color is: red
多繼承的方法執行順序
從上面的案例我們可以看出:
Mango類即使初始化順序先初始化了Plant,再初始化Fruit類,但是執行的同名方法的順序仍然按定義該類是括號中的繼承順序;
Watermelon類也是按括號中的繼承順序來執行同名方法,但是執行的Melon類同名方法中,即使有該執行的Melon類的父類Fruit類也有同名方法,但還是優先執行該Melon類,這與一般的單繼承規則一致。 總結如下:
- 定義派生類時,需要注意圓括號中繼承父類的順序,若是父類中有相同的方法名,而在子類使用時未指定,python從左至右搜索 即方法在子類中未找到時,從左到右查找父類中是否包含方法;
- 如果繼承的父類中,該父類還繼承了別的父類,則調用同名方法時是調用最先訪問到的同名方法;
- 支持多層父類繼承,子類會繼承父類所有的屬性和方法,包括父類的父類的所有屬性 和 方法。
多繼承時通過super方法初始化
單繼承中我們往往會使用super方法來初始化父類init方法,因爲super方法可以防止同一個類被實例化多次。 但是這在多繼承中往往會出現問題,我們把上面的例子改爲如下demo:
class Plant(object):
def __init__(self, color):
print("init Plant start")
self.color = color
print("init Plant end")
def show(self):
print("The Plant color is:", self.color)
class Fruit(object):
def __init__(self, color):
print("init Fruit start")
self.color = color
print("init Fruit end")
def show(self):
print("The Fruit color is:", self.color)
class Melon(Fruit):
def __init__(self, color):
print("init Melon start")
super(Melon, self).__init__(color)
self.color = color
print("init Melon end")
def show(self):
print("The Melon color is:", self.color)
class Mango(Fruit, Plant):
def __init__(self, color):
print("init Mango start")
super(Mango, self).__init__(color)
self.color = color
print("init Mango end")
def show_color(self):
print("The Mango color is:", self.color)
class Watermelon(Melon, Plant):
def __init__(self, color):
print("init Watermelon start")
super(Watermelon, self).__init__(color)
self.color = color
print("init Watermelon end")
def show_color(self):
print("The Watermelon color is:", self.color)
if __name__ == "__main__":
mango = Mango("yellow")
mango.show()
watermelon = Watermelon("red")
watermelon.show()
可以看到,我們只是把上面Mango類和Watermelon類初始化父類的方法改爲了super方式,但是執行結果卻如下:
init Mango start
init Fruit start
init Fruit end
init Mango end
The Fruit color is: yellow
init Watermelon start
init Melon start
init Fruit start
init Fruit end
init Melon end
init Watermelon end
The Melon color is: red
可以看到,兩個實例中繼承順序排在後面的Plant類都沒有被實例化,所以多繼承中採用super是會有問題的,它只會實例化一次,所以super類並不適合使用在多繼承場合。 其實導致這個問題的原因是Python中的一個MRO(Method Resolution Order)繼承調用順序的算法,我們要修復上面的問題,可以在Fruit類中加入super方法,把Fruit類改爲:
class Fruit(object):
def __init__(self, color):
super().__init__(color)
print("init Fruit start")
self.color = color
print("init Fruit end")
def show(self):
print("The Fruit color is:", self.color)
就可以達到實例化兩個類的作用了
關於Python的mro方法
Python 中針對 類 提供了一個內置屬性 mro 可以查看方法搜索順序 MRO 是 method resolution order,主要用於在多繼承時判斷 方法、屬性 的調用 路徑 Python2.3以上,MRO算法就已經改爲了廣度優先,防止最頂級的類被多次實例化而耗費資源:
從C.mro的值可以看出, Python的方法解析優先級從高到低爲:
- 實例本身(instance)
- 類(class)
- super class, 繼承關係越近, 越先定義, 優先級越高. 例如我們執行print(Mango.__mro__)會得到如下返回: (, , , )
Python多繼承的注意事項
在單繼承場景:super().init等同於類名.init;
在多繼承場景,super方法只能保證第一個父類被初始化,除非每一個繼承的父類都實現super初始化,最終初始化了根類Object類;
另外,子類從多個父類派生,而子類又沒有自己的構造函數時:
1. 按順序繼承,哪個父類在最前面且它又有自己的構造函數,就繼承它的構造函數;
2. 如果最前面第一個父類沒有構造函數,則繼承第2個的構造函數,第2個沒有的話,再往後找,以此類推。
【雲棲號在線課堂】每天都有產品技術專家分享!
課程地址:https://yqh.aliyun.com/live立即加入社羣,與專家面對面,及時瞭解課程最新動態!
【雲棲號在線課堂 社羣】https://c.tb.cn/F3.Z8gvnK
原文發佈時間:2020-08-03
本文作者:henry_czh
本文來自:“掘金”,瞭解相關信息可以關注“掘金”