一、
繼承有兩種用途:
""" 一:繼承基類的方法,並且做出自己的改變或者擴展(代碼重用) 二:聲明某個子類兼容於某基類,定義一個接口類Interface,接口類中定義了一些接口名(就是函數名) 且並未實現接口的功能,子類繼承接口類,並且實現接口中的功能 三、接口隔離原則:使用多個專門的接口,而不使用單一的總接口。即客戶端不應該依賴那些不需要的接口 """
""" 接口類:基於同一個接口實現的類 剛好滿足接口隔離原則 面向對象開發的思想 規范 接口類,python 原生不支持 在python中,並沒有接口類這種東西,即便不通過專門的模塊定義接口,我們也應該有一些基本的概念 """
一、接口類單繼承
我們來看一段代碼去了解為什么需要接口類
class Alipay: def pay(self,money): print('支付寶支付了') class Apppay: def pay(self,money): print('蘋果支付了') class Weicht: def pay(self,money): print('微信支付了') def pay(payment,money): # 支付函數,總體負責支付,對應支付的對象和要支付的金額 payment.pay(money) p=Alipay() pay(p,200) #支付寶支付了
這段代碼,實現了一個有趣的功能,就是通過一個總體的支付函數,實現了不同種類的支付方式,不同是支付方式作為對象,傳入函數中
但是開發中容易出現一些問題,那就是類中的函數名不一致,就會導致調用的時候找不到類中對應方法,例題如下:
class Alipay: def paying(self,money): #這里類的方法可能由於程序員的疏忽,寫的不是一致的pay,導致后面調用的時候找不到pay print('支付寶支付了') class Apppay: def pay(self,money): print('蘋果支付了') class Weicht: def pay(self,money): print('微信支付了') def pay(payment,money): # 支付函數,總體負責支付,對應支付的對象和要支付的金額 payment.pay(money) p=Alipay() #不報錯 pay(p,200) #調用執行就會報錯,'Alipay' object has no attribute 'pay'
這時候怎么辦呢?可以手動拋異常:NotImplementedError來解決開發中遇到的問題
class payment: def pay(self): raise NotImplementedError #手動拋異常 class Alipay: def paying(self, money): # 這里類的方法不是一致的pay,導致后面調用的時候找不到pay print('支付寶支付了') def pay(payment, money): # 支付函數,總體負責支付,對應支付的對象和要支付的金額 payment.pay(money) p = Alipay() # 不報錯 pay(p, 200) #調用的時候才會報錯 'Alipay' object has no attribute 'pay'
也可以借用abc模塊來處理這種錯誤
from abc import abstractmethod, ABCMeta #接口類中定義了一些接口名:Pay,且並未實現接口的功能,子類繼承接口類,並且實現接口中的功能 class Payment(metaclass=ABCMeta): #抽象出的共同功能Pay @abstractmethod def pay(self,money):pass #這里面的pay 來源於下面類中的方法pay,意思把這個方法規范為統一的標准,另外建一個規范類Payment class Alipay(Payment): def paying(self, money): #這里出現paying和我們規范的pay不一樣,那么在實例化 Alipay的時候就會報錯 print('支付寶支付了') class Weicht(Payment): def pay(self,money): print('微信支付了') def pay(pay_obj,money): pay_obj.pay(money) p=Alipay() #實例化的時候就會報錯 Can't instantiate abstract class Alipay with abstract methods pay 之前兩個例子都是在執行的時候報錯,這里不一樣的是實例化就會知道是哪里發生錯誤了
""" 總結:用abc模塊裝飾后,在實例化的時候就會報錯,那么當我們代碼很長的時候,就可以早一點預知錯誤,所以以后在接口類類似問題中用這個模塊 接口繼承實質上是要求“做出一個良好的抽象,這個抽象規定了一個兼容接口,使得外部調用者無需關心具體細節, 可一視同仁的處理實現了特定接口的所有對象”——這在程序設計上,叫做歸一化。 """
二、接口類多繼承
from abc import abstractmethod,ABCMeta class Walk_animal(meteaclass=ABCMeta): @abstractmethod def walk(self): print('walk') class Swim_animal(meteaclass=ABCMeta): @abstractmethod def swim(self):pass class Fly_animal(metaclass=ABCMeta) @abstractmethod def fly(self):pass #如果正常一個老虎有跑和跑的方法的話,我們會這么做 class Tiger: def walk(self):pass def swim(self):pass #但是我們使用接口類多繼承的話就簡單多了,並且規范了相同功能 class Tiger(Walk_animal,Swim_animal):pass #如果此時再有一個天鵝swan,會飛,走,游泳 那么我們這么做 class Swan(Walk_animal,Swim_animal, Fly_animal):pass # 這就是接口多繼承

接口提取了一群類共同的函數,可以把接口當做一個函數的集合。
然后讓子類去實現接口中的函數。
這么做的意義在於歸一化,什么叫歸一化,就是只要是基於同一個接口實現的類,那么所有的這些類產生的對象在使用時,從用法上來說都一樣。
歸一化,讓使用者無需關心對象的類是什么,只需要的知道這些對象都具備某些功能就可以了,這極大地降低了使用者的使用難度。
比如:我們定義一個動物接口,接口里定義了有跑、吃、呼吸等接口函數,這樣老鼠的類去實現了該接口,松鼠的類也去實現了該接口,由二者分別產生一只老鼠和一只松鼠送到你面前,即便是你分別不到底哪只是什么鼠你肯定知道他倆都會跑,都會吃,都能呼吸。
再比如:我們有一個汽車接口,里面定義了汽車所有的功能,然后由本田汽車的類,奧迪汽車的類,大眾汽車的類,他們都實現了汽車接口,這樣就好辦了,大家只需要學會了怎么開汽車,那么無論是本田,還是奧迪,還是大眾我們都會開了,開的時候根本無需關心我開的是哪一類車,操作手法(函數調用)都一樣
為何要用接口
三、抽象類
#抽象類 # 抽象類的本質還是類, # 指的是一組類的相似性,包括數據屬性(如all_type)和函數屬性(如read、write),而接口只強調函數屬性的相似性
""" 1.抽象類是一個介於類和接口直接的一個概念,同時具備類和接口的部分特性,可以用來實現歸一化設計 2.在繼承抽象類的過程中,我們應該盡量避免多繼承; 3.而在繼承接口的時候,我們反而鼓勵你來多繼承接口 # 一般情況下 單繼承 能實現的功能都是一樣的,所以在父類中可以有一些簡單的基礎實現 # 多繼承的情況 由於功能比較復雜,所以不容易抽象出相同的功能的具體實現寫在父類中 """

如果說類是從一堆對象中抽取相同的內容而來的,那么抽象類就是從一堆類中抽取相同的內容而來的,內容包括數據屬性和函數屬性。
從設計角度去看,如果類是從現實對象抽象而來的,那么抽象類就是基於類抽象而來的。
從實現角度來看,抽象類與普通類的不同之處在於:抽象類中有抽象方法,該類不能被實例化,只能被繼承,且子類必須實現抽象方法。這一點與接口有點類似,但其實是不同的
#一切皆文件 import abc #利用abc模塊實現抽象類 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定義抽象方法,無需實現功能 def read(self): '子類必須定義讀功能' pass @abc.abstractmethod #定義抽象方法,無需實現功能 def write(self): '子類必須定義寫功能' pass # class Txt(All_file): # pass # # t1=Txt() #報錯,子類沒有定義抽象方法 class Txt(All_file): #子類繼承抽象類,但是必須定義read和write方法 def read(self): print('文本數據的讀取方法') def write(self): print('文本數據的讀取方法') class Sata(All_file): #子類繼承抽象類,但是必須定義read和write方法 def read(self): print('硬盤數據的讀取方法') def write(self): print('硬盤數據的讀取方法') class Process(All_file): #子類繼承抽象類,但是必須定義read和write方法 def read(self): print('進程數據的讀取方法') def write(self): print('進程數據的讀取方法') wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #這樣大家都是被歸一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)
四、擴展:
不管是抽象類還是接口類 : 面向對象的開發規范 所有的接口類和抽象類都不能實例化
java :
java里的所有類的繼承都是單繼承,所以抽象類完美的解決了單繼承需求中的規范問題
但對於多繼承的需求,由於java本身語法的不支持,所以創建了接口Interface這個概念來解決多繼承的規范問題
python:
python中沒有接口類 :
python中自帶多繼承 所以我們直接用class來實現了接口類
python中支持抽象類 : 一般情況下 單繼承 不能實例化
且可以實現python代碼
五、注意
""" 1.多繼承問題 在繼承抽象類的過程中,我們應該盡量避免多繼承; 而在繼承接口的時候,我們反而鼓勵你來多繼承接口 2.方法的實現 在抽象類中,我們可以對一些抽象方法做出基礎實現; 而在接口類中,任何方法都只是一種規范,具體的功能需要子類實現 """