Python面向對象特性 - 繼承
?
什麽是繼承
繼承是創建類的一種方式,在 Python中支持多繼承,即在創建類的時候可以繼承一個或者多個父類。
在繼承關系中,被繼承的類通常稱為父類(或超類,基類),新建的類則稱為子類(或派生類)。
?
繼承的優勢在於可以有效地重用代碼,提高代碼的可讀性~
?
繼承示例:
class Fu_1: # 父類 pass class Fu_2: # 父類 pass class Zi_1(Fu_1): # 單繼承 pass class Zi_2(Fu_1, Fu_2): # 多繼承 pass
?
上述示例中,Fu_1 和 Fu_2 沒有繼承任何類,在 Python3 中,這樣就會默認繼承object類,而在 Python2 中,默認不會繼承 object類,註意區分 ~
?
可通過 類的內置屬性 __bases__ 查看這個類 繼承的所有父類
print(Zi_1.__bases__)
print(Zi_2.__bases__)
# 輸出結果:
(<class ‘__main__.Fu_1‘>,)
(<class ‘__main__.Fu_1‘>, <class ‘__main__.Fu_2‘>)
重用與派生
重用
在開發過程中,若新建的一個類和已創建的另一個類 屬性及方法大致相同,則可以讓新建的類(子類)繼承已創建的類(父類),這樣子類會繼承父類的所有屬性,包括數據屬性和函數屬性,實現了代碼重用,代碼變得簡潔,可以有效縮短開發周期~
class Father:
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print(‘Hello !‘)
class Son(Father):
pass
p = Son(‘baby‘, 19)
p.say()
# 輸出結果:
Hello !
?
派生
在繼承過程中,子類也可以添加或者重新定義這些屬性,當父類和子類中有同名的屬性時(包括數據屬性和函數屬性),會先調用子類中的屬性(操作的是子類的實例化對象)
class Father:
city = ‘NB‘
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print(‘Hello ! ‘ + self.city)
class Son(Father):
city = ‘HZ‘
def say(self):
print(‘你好 ~ ‘ + self.city)
def eat(self):
pass
p = Son(‘baby‘, 19)
p.say()
# 輸出結果:
你好 ~ HZ
super
在子類的函數中,若是要重用父類中某個函數的功能,可以直接通過 super 來調用父類中的函數,當然也可以通過 類名.func() 來調用,只不過這樣與調用普通函數無異。這個經常使用在需要對父類的同名方法進行擴展的場景~
class Father:
def say(self):
print(‘Hello !‘)
def introduce(self):
print(‘Father‘)
class Son(Father):
def say(self):
super().say()
# Father.say(self) # 通過 類名.func(),輸出結果一致
print(‘你好 ~‘)
p = Son()
p.say()
# 輸出結果
Hello !
你好 ~
上述示例中,使用 super的時候省略了2個參數:Son(當前類名稱,註意不是父類),self(當前對象)
super().say()
# 等同於
super(Son, self).say()
由於 super 方法中已經默認傳遞了self參數,所以後面的函數不需要再次傳遞self~
?
註意:super關鍵字只在新式類中有,Python3中所有的類都是新式類...
?
在子類的函數中使用super方法,不一定僅調用同名的父類函數,也可以調用其他的父類函數~
def say(self):
super().introduce()
print(‘你好 ~‘)
?
如下示例中子類對父類的 init方法 進行了擴展,這是一種較為常用的使用方式~
class Father:
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print(‘Hello !‘)
class Son(Father):
def __init__(self, name, age, hobby):
super().__init__(name, age)
self.hobby = hobby
def say(self):
super().say()
print(‘你好 ~‘)
?
super方法不光可以在類的內部使用,也可以在類的外部的使用,在類的外部使用時,super方法不可以省略參數
# 外部使用super
p = Son()
super(Son, p).say()
# 輸出結果:
Hello !
Python中多繼承的繼承順序
上面已經說過,Python3 中所有的類都是新式類,新建的類沒有繼承任何類的時候,會默認繼承 object 類。
在 Python2中,經典類和新式類並存,新建的類若是沒有繼承任何類,則這個類為經典類,只有顯示地繼承了 object 類或其子類,這個類才是新式類~
# Python2中
class C1: # 經典類
pass
class C2(C1): # 經典類
pass
class C3(object): # 新式類
pass
# Python3中
class N1: # 新式類
pass
class N2(N1): # 新式類
pass
class N3(object): # 新式類
pass
?
在Python中支持多繼承,新式類和經典類的繼承順序有所差異,以下以鉆石繼承為例給出示例:
class A(object):
def fun(self):
print(‘from A‘)
class B(A):
def fun(self):
print(‘from B‘)
class C(A):
def fun(self):
print(‘from C‘)
class D(B):
def fun(self):
print(‘from D‘)
class E(C):
def fun(self):
print(‘from E‘)
class F(D, E):
# def fun(self):
# print(‘from F‘)
pass
上述多個類的繼承關系如下圖所示:
當前環境為Python3,即新式類,可通過內置的__mro__方法查看繼承順序:
f1 = F()
# f1.fun()
print(F.__mro__)
# 輸出結果:
(<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,廣度優先。
那什麽是繼承順序呢?就是尋找父類的順序,例如這裏調用 f1對象(F類的實例化對象)的 fun方法,若F類中沒有這個方法,就去D類中尋找,若D類中也沒有,就去B類中尋找,然後是E類,C類,A類,即按照 MRO列表上 從左到右查找基類~
?
若這裏的類是經典類,則繼承順序為:F->D->B->A->E->C ,深度優先~
廣度優先 與 深度優先 的區別在於,廣度優先算法在查找基類的時候,若之後能找到的,則之後再進行查找,若之後找不到的,現在就去查找。例如,通過E,C也能再次找到A,則先不找A,但是通過E,C不能再次找到B,所以查找D之後就找B,查找B之後不會去查找A,而是在C之後再去查找A~
?
再看如下示例,由於B和C只能通過一條途徑找到,所以新式類和經典類的繼承順序一致:
新式類繼承順序:F->D->B->E->C
經典類繼承順序:F->D->B->E->C
結合super的多繼承順序
若是在鉆石繼承中用到了 super 關鍵字,super 會去調用父類的對應方法,但是 super 的本質並不是直接找父類,而是根據調用者的節點位置的廣度優先順序來查找的。即 按照廣度優先的繼承順序找到上一個類~
Tip:super 關鍵字只有在新式類中有,所以肯定是按照廣度優先的繼承順序來進行查找的~
?
class A:
def func(self):
print(‘A‘)
class B(A):
def func(self):
super().func()
print(‘B‘)
class C(A):
def func(self):
super().func()
print(‘C‘)
class D(B, C):
def func(self):
super().func()
print(‘D‘)
d = D()
d.func()
# 輸出結果:
A
C
B
D
上述多個類的繼承關系如下圖所示:
當前使用的是新式類,繼承順序為:D->B->C->A(經典類的繼承順序為:F->B->A->C)
?
其中super的調用過程如下:
D類的 func() 方法中的 super().func() 會調用 B類的 func() 方法,B類的 func() 方法中的super().func() 會調用 C類的 func() 方法,C類的 func() 方法中的 super().func() 會調用 A類的 func() 方法~
?
當然在單繼承中不會有這樣的問題~
?
.................^_^
Python面向對象特性 - 繼承