python--接口類與抽象類


一、

繼承有兩種用途:
"""
 一:繼承基類的方法,並且做出自己的改變或者擴展(代碼重用)
 二:聲明某個子類兼容於某基類,定義一個接口類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.方法的實現
在抽象類中,我們可以對一些抽象方法做出基礎實現;
而在接口類中,任何方法都只是一種規范,具體的功能需要子類實現
"""

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM