淺析Python中接口與抽象基類的使用
接觸Python
比較早的朋友可能都有這樣的體會,Python
語言雖然也支持面向對象的編程方式,
但是,不像那些純面向對象的語言(比如Java
和.NET
)那樣嚴格和規(guī)范。
隨著項目的規(guī)模逐步擴大之后,想要以一種清晰、可維護和可擴展的方式定義和實施對象的行為就變得越來越困難。
今天介紹的Python中兩個為面向對象編程提供的強大工具:接口和抽象基類。
它們的英文分別為Protocols
和ABC
(Abstract Base Classes
)。
Protocols
是Python3.8
才開始引入的,有的地方也翻譯成協(xié)議,我感覺翻譯成接口更熟悉一些。
ABC
引入的比較早,在Python3
之后得到了改進和優(yōu)化,現(xiàn)在和其他語言的抽象類相比,差別不大。
1. 接口(Protocols)
Python3.8
開始在類型模塊中引入的接口Protocols
的概念,它提供了一種無需顯式繼承即可定義接口的方法。
接口Protocols
定義了一組方法或屬性,只要一個對象實現(xiàn)了這些方法或屬性,就被視為滿足該接口。
下面通過一個示例來幫助理解Protocols
的使用,如果有面向對象編程的經(jīng)驗,很容易就能理解這個概念。
這個示例來自最近用的一個量化交易系統(tǒng)的一部分,這個功能需要從不同的來源獲取數(shù)據(jù),然后進行分析,最后將分析結果以不同的方式輸出。
這三個步驟(獲取數(shù)據(jù),分析和輸出)中,
假設獲取數(shù)據(jù)的來源有網(wǎng)絡(API
),文件(CSV
)和數(shù)據(jù)庫3種;
分析的步驟是統(tǒng)一的;輸出的方式假設也有多種,比如郵件,短信等等。
根據(jù)這個描述,使用Protocols
構建的獲取數(shù)據(jù)和分析部分的代碼如下:
(輸出的部分暫時不管)
from typing import Protocol # 輸入數(shù)據(jù)的接口 class InputData(Protocol): def get_data(self) -> str: pass class APIHandler: def get_data(self) -> str: print("get_data from API") return "get data from API" class CSVHandler: def get_data(self) -> str: print("get_data from CSV") return "get data from CSV" class SqliteHandler: def get_data(self) -> str: print("get_data from SQLITE DATABASE") return "get data from SQLITE DATABASE" # 分析數(shù)據(jù) def analysis(i: InputData): data = i.get_data() print("開始處理數(shù)據(jù)...")
InputData
繼承了Protocol
,其中定義了接口的函數(shù)get_data
。
只要實現(xiàn)了get_data
的class
,比如APIHandler
,CSVHandler
和SqliteHandler
,都可以當做InputData
類型。
從代碼可以看出,我們不需要用APIHandler
去繼承InputData
,只要實現(xiàn)InputData
中的所有方法就可以了。
這種靈活性確保了系統(tǒng)的可擴展性,我們可以添加新的數(shù)據(jù)源類型,而無需修改現(xiàn)有代碼。
接下來我們測試上面的代碼是否可以正常使用:
if __name__ == "__main__": i = APIHandler() analysis(i) print("\n") i = CSVHandler() analysis(i) print("\n") i = SqliteHandler() analysis(i) print("\n")
運行結果:
$ python.exe .\protocol_abc.py
get_data from API
開始處理數(shù)據(jù)...
get_data from CSV
開始處理數(shù)據(jù)...
get_data from SQLITE DATABASE
開始處理數(shù)據(jù)...
2. 抽象基類(ABC)
Protocol
非常具有靈活性,但有時我們需要更結構化的方法,這就是抽象基類 (ABC
) 的用武之地。
ABC
是一種通過定義子類必須實現(xiàn)的嚴格接口來強制執(zhí)行一致行為的工具。
與Protocol
不同,ABC
需要顯式繼承,因此當我們希望在代碼中明確定義層次結構時,ABC
是更好的選擇。
接著實現(xiàn)上一節(jié)示例中的輸出部分,每種不同的輸出需要不同的配置,
比如輸出到郵件需要先配置郵箱的賬號信息,輸出到短信需要配置手機信息等等。
在這里,我們使用 ABC
來實現(xiàn)輸出的基類。
# 輸出的抽象基類 class OutputResult(ABC): def __init__(self): self.settings: dict = {} @abstractmethod def send(self, data: str): pass @abstractmethod def config(self, settings: dict): pass class OutputMail(ABC): def send(self, data: str): print(f"send {data} to {self.settings['name']}") def config(self, settings: dict): self.settings = settings class OutputMessage(ABC): def send(self, data: str): print(f"send {data} to {self.settings['name']}") def config(self, settings: dict): self.settings = settings
這里使用抽象基類的原因是輸出時,并不是簡單的調(diào)用send
方法就可以的,還需要配置輸出的參數(shù),
所以用帶有結構的抽象基類更好。
加上輸出之后,上一節(jié)中的分析函數(shù)也改為:
# 分析數(shù)據(jù) def analysis(i: InputData, o: OutputResult): data = i.get_data() print("開始處理數(shù)據(jù)...") data = data.replace("get data from ", "") o.send(data)
測試的代碼如下:
if __name__ == "__main__": i = APIHandler() o = OutputMail() o.config({"name": "aaa@bbb.com"}) analysis(i, o) print("\n") i = CSVHandler() o = OutputMessage() o.config({"name": "13911123456"}) analysis(i, o) print("\n") i = SqliteHandler() o = OutputMail() o.config({"name": "xyz@www.com"}) analysis(i, o) print("\n")
運行結果:
$ python.exe .\protocol_abc.py
get_data from API
開始處理數(shù)據(jù)...
send API to aaa@bbb.com
get_data from CSV
開始處理數(shù)據(jù)...
send CSV to 13911123456
get_data from SQLITE DATABASE
開始處理數(shù)據(jù)...
send SQLITE DATABASE to xyz@www.com
3. 兩者的選擇
當我們在實際的開發(fā)設計中,應該如何選擇Protocol
和ABC
呢?
其實,Protocol
和ABC
之間的選擇并不是非黑即白,這通常取決于項目的背景和你的目標。
一般來說,下面這些情況我們優(yōu)先選擇使用Protocol
:
- 你正在使用現(xiàn)有代碼或希望集成第三方庫
- 靈活性是首要任務,你不想強制執(zhí)行嚴格的層次結構
- 來自不相關的類層次結構的對象需要共享行為
而下面這些情況,優(yōu)先選擇ABC
:
- 正在從頭開始設計一個系統(tǒng),并且需要加強結構
- 類之間的關系是可預測的,并且繼承是有意義的
- 共享功能或默認行為可以減少重復并提高一致性
4. 總結
總的來說,Protocol
和ABC
不是互相競爭的兩種工具,它們是互補的。
我使用Protocol
將類型安全改造到遺留系統(tǒng)中,而不需要大量重構。
另一方面,如果我在從頭開始構建一個結構和一致性至關重要的系統(tǒng)時,會使用 ABC
。
在決定使用哪個時,請考慮項目的靈活性需求和長期目標。
Protocol
提供靈活性和無縫集成,而 ABC
有助于建立結構和一致性。
通過了解它們各自的優(yōu)勢,你可以選擇合適的方式來構建健壯、可維護的 Python 系統(tǒng)。
到此這篇關于淺析Python中接口與抽象基類的使用的文章就介紹到這了,更多相關Python接口與抽象基類內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Python?pyecharts?Boxplot箱線圖的實現(xiàn)
本文主要介紹了Python?pyecharts?Boxplot箱線圖的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-05-05Python 將 QQ 好友頭像生成祝福語的實現(xiàn)代碼
這篇文章主要介紹了用 Python 將 QQ 好友頭像生成祝福語的實現(xiàn)代碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05python中windows鏈接linux執(zhí)行命令并獲取執(zhí)行狀態(tài)的問題小結
這篇文章主要介紹了python中windows鏈接linux執(zhí)行命令并獲取執(zhí)行狀態(tài),由于工具是pyqt寫的所以牽扯到用python鏈接linux的問題,這里記錄一下一些碰到的問題,需要的朋友可以參考下2022-11-11Python實現(xiàn)計算兩個指定日期相差幾年幾月幾日
這篇文章主要為大家詳細介紹了如何使用Python實現(xiàn)計算兩個日期之間相差多少年,多少月,多少天,文中的的示例代碼講解詳細,需要的可以參考下2024-02-02