一、繼承概述
面向對象編程 (OOP) 語言的一個主要功能就是“繼承”。繼承是指這樣一種能力:它可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展。
通過繼承創建的新類稱爲“子類”或“派生類”,被繼承的類稱爲“基類”、“父類”或“超類”,繼承的過程,就是從一般到特殊的過程。在某些 OOP 語言中,一個子類可以繼承多個基類。但是一般情況下,一個子類只能有一個基類,要實現多重繼承,可以通過多級繼承來實現。
繼承概念的實現方式主要有2類:實現繼承、接口繼承。
實現繼承是指使用基類的屬性和方法而無需額外編碼的能力。
接口繼承是指僅使用屬性和方法的名稱、但是子類必須提供實現的能力(子類重構爹類方法)。
在考慮使用繼承時,有一點需要注意,那就是兩個類之間的關係應該是“屬於”關係。例如,Employee 是一個人,Manager 也是一個人,因此這兩個類都可以繼承 Person 類。但是 Leg 類卻不能繼承 Person 類,因爲腿並不是一個人。
二、實現繼承
簡單的繼承
class Person:
def walk(self):
print("person is walking")
class Man(Person):
def talk(self):
print("man is talking")
p1 = Man()
print(Man.__dict__)
p1.walk()
p1.talk()
####結果
#{'__doc__': None, 'talk': <function man.talk at 0x10330dae8>, '__module__': '__main__'}
#person is walking
#man is talking
可以看出Man類繼承了Person類的方法,但是在Man類的屬性字典中除了自己定義的talk方法並沒有walk方法,因此只有子類中沒有找到類似的方法時纔會去父類中尋找
方法重寫
class Person:
def walk(self):
print("person is walking")
class Man(Person):
def walk(self):
print("man is walking")
def talk(self):
print("man is talking")
p1 = Man()
x1 = Person()
p1.walk()
x1.walk()
####結果
#{'__doc__': None, 'talk': <function man.talk at 0x10330dae8>, '__module__': '__main__'}
#man is walking
#person is walking
子類可以對父類的方法進行重寫,而不會覆蓋父類的方法
使用父類的方法
方法一
父類名.方法名
class Car:
def __init__(self,size,type,price):
self.size = size
self.type = type
self.price = price
class Battery(Car):
def __init__(self,size,type,price,battery):
Car.__init__(self,size,type,price)
self.battery = battery
def show_info(self):
print("%s的電池電量爲%s"%(self.type,self.battery))
mycar = Battery('small','E1','100w','8000A/h')
mycar.show_info()
#E1的電池電量爲8000A/h
使用此類調用父類的方法有缺陷,如果一個父類被若干個子類繼承,父類一修改類名,則會導致代碼有多處都需要修改,因此介紹方法二
方法二
super().方法名
class Car:
def __init__(self,size,type,price):
self.size = size
self.type = type
self.price = price
class Battery(Car):
def __init__(self,size,type,price,battery):
super().__init__(size,type,price)
#使用此種調用方法,也不用傳入self參數
self.battery = battery
def show_info(self):
print("%s的電池電量爲%s"%(self.type,self.battery))
mycar = Battery('small','E1','100w','8000A/h')
mycar.show_info()
#E1的電池電量爲8000A/h
三、接口繼承
聲明某個子類兼容於某個父類,定義一個接口類,子類繼承接口類,並且實現接口中定義的方法。
在實踐中,接口繼承比較常見並且實用,因爲實現繼承雖然減少了代碼複用,但是這樣子類和父類的耦合度較高。
而接口繼承規定了一個兼容接口(接口就是一種方法),使得外部調用這可以使用統一的接口調用,這種方法叫做歸一化設計。
linux中的一切皆文件,是此編程思想的最典型的例子。
import abc
class All_file(metaclass=abc.ABCMeta): # All_file爲一個接口類
@abc.abstractmethod #此裝飾器是爲了子類必須對此接口方法進行重寫,否則無法進行實例化
def read(self): # read爲接口方法,沒有任何實際方法
pass
@abc.abstractmethod
def wirte(self): # 同樣也是接口方法
pass
class Disk(All_file):
def read(self):
print("Disk is reading")
def wirte(self):
print("Disk is wirteing")
class Mem(All_file):
def read(self):
print("Mem is reading")
def wirte(self):
print("Mem is wirteing")
四、繼承順序
若一個類繼承了多個類,那麼如果類在自己的類中找不到方法時,往父類中尋找方法的順序是不同的。其中新式類和經典類的尋找順序也不同
新式類指的是繼承了object類的子類,而經典類在定義時沒有繼承任何類。在python3中定義的類都爲新式類
#新式類的定義
class Nc(obejct):
pass
#經典類的定義
class Oc:
pass
新式類的繼承順序
class A:
def test(self):
print("這是A的方法")
class B(A):
def test(self):
print("這是B的方法")
class C(A):
def test(self):
print("這是C的方法")
class D(B):
def test(self):
print("這是D的方法")
class E(C):
def test(self):
print("這是E的方法")
class F(D,E):
def test(self):
print("這是F的方法")
f1 = F()
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,頂級的父類是最後被尋找的,另一種被廣泛的說法說這叫做廣度尋找
經典類的繼承順序
經典類的繼承順序和新式類不太一樣 ,繼承順序爲F-D-B-A-E-C,這一般被叫做深度尋找。先從一個分支直接尋找至頂級父類,若還沒有得到結果,則尋找分支。