淺析Python中接口與抽象基類的使用
接觸Python比較早的朋友可能都有這樣的體會,Python語言雖然也支持面向?qū)ο蟮木幊谭绞剑?/p>
但是,不像那些純面向?qū)ο蟮恼Z言(比如Java和.NET)那樣嚴(yán)格和規(guī)范。
隨著項目的規(guī)模逐步擴(kuò)大之后,想要以一種清晰、可維護(hù)和可擴(kuò)展的方式定義和實施對象的行為就變得越來越困難。
今天介紹的Python中兩個為面向?qū)ο缶幊烫峁┑膹?qiáng)大工具:接口和抽象基類。
它們的英文分別為Protocols和ABC(Abstract Base Classes)。
Protocols是Python3.8才開始引入的,有的地方也翻譯成協(xié)議,我感覺翻譯成接口更熟悉一些。
ABC引入的比較早,在Python3之后得到了改進(jìn)和優(yōu)化,現(xiàn)在和其他語言的抽象類相比,差別不大。
1. 接口(Protocols)
Python3.8開始在類型模塊中引入的接口Protocols的概念,它提供了一種無需顯式繼承即可定義接口的方法。
接口Protocols定義了一組方法或?qū)傩?,只要一個對象實現(xiàn)了這些方法或?qū)傩裕捅灰暈闈M足該接口。
下面通過一個示例來幫助理解Protocols的使用,如果有面向?qū)ο缶幊痰慕?jīng)驗,很容易就能理解這個概念。
這個示例來自最近用的一個量化交易系統(tǒng)的一部分,這個功能需要從不同的來源獲取數(shù)據(jù),然后進(jìn)行分析,最后將分析結(jié)果以不同的方式輸出。
這三個步驟(獲取數(shù)據(jù),分析和輸出)中,
假設(shè)獲取數(shù)據(jù)的來源有網(wǎng)絡(luò)(API),文件(CSV)和數(shù)據(jù)庫3種;
分析的步驟是統(tǒng)一的;輸出的方式假設(shè)也有多種,比如郵件,短信等等。
根據(jù)這個描述,使用Protocols構(gòu)建的獲取數(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,都可以當(dāng)做InputData類型。
從代碼可以看出,我們不需要用APIHandler去繼承InputData,只要實現(xiàn)InputData中的所有方法就可以了。
這種靈活性確保了系統(tǒng)的可擴(kuò)展性,我們可以添加新的數(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")
運行結(jié)果:
$ 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非常具有靈活性,但有時我們需要更結(jié)構(gòu)化的方法,這就是抽象基類 (ABC) 的用武之地。
ABC 是一種通過定義子類必須實現(xiàn)的嚴(yán)格接口來強(qiáng)制執(zhí)行一致行為的工具。
與Protocol不同,ABC 需要顯式繼承,因此當(dāng)我們希望在代碼中明確定義層次結(jié)構(gòu)時,ABC是更好的選擇。
接著實現(xiàn)上一節(jié)示例中的輸出部分,每種不同的輸出需要不同的配置,
比如輸出到郵件需要先配置郵箱的賬號信息,輸出到短信需要配置手機(jī)信息等等。
在這里,我們使用 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é)構(gòu)的抽象基類更好。
加上輸出之后,上一節(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")
運行結(jié)果:
$ 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. 兩者的選擇
當(dāng)我們在實際的開發(fā)設(shè)計中,應(yīng)該如何選擇Protocol和ABC呢?
其實,Protocol和ABC之間的選擇并不是非黑即白,這通常取決于項目的背景和你的目標(biāo)。
一般來說,下面這些情況我們優(yōu)先選擇使用Protocol:
- 你正在使用現(xiàn)有代碼或希望集成第三方庫
- 靈活性是首要任務(wù),你不想強(qiáng)制執(zhí)行嚴(yán)格的層次結(jié)構(gòu)
- 來自不相關(guān)的類層次結(jié)構(gòu)的對象需要共享行為
而下面這些情況,優(yōu)先選擇ABC:
- 正在從頭開始設(shè)計一個系統(tǒng),并且需要加強(qiáng)結(jié)構(gòu)
- 類之間的關(guān)系是可預(yù)測的,并且繼承是有意義的
- 共享功能或默認(rèn)行為可以減少重復(fù)并提高一致性
4. 總結(jié)
總的來說,Protocol和ABC不是互相競爭的兩種工具,它們是互補的。
我使用Protocol將類型安全改造到遺留系統(tǒng)中,而不需要大量重構(gòu)。
另一方面,如果我在從頭開始構(gòu)建一個結(jié)構(gòu)和一致性至關(guān)重要的系統(tǒng)時,會使用 ABC。
在決定使用哪個時,請考慮項目的靈活性需求和長期目標(biāo)。
Protocol提供靈活性和無縫集成,而 ABC 有助于建立結(jié)構(gòu)和一致性。
通過了解它們各自的優(yōu)勢,你可以選擇合適的方式來構(gòu)建健壯、可維護(hù)的 Python 系統(tǒng)。
到此這篇關(guān)于淺析Python中接口與抽象基類的使用的文章就介紹到這了,更多相關(guān)Python接口與抽象基類內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python通過colorama模塊在控制臺輸出彩色文字的方法
這篇文章主要介紹了python通過colorama模塊在控制臺輸出彩色文字的方法,實例分析了colorama模塊的功能及相關(guān)使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-03-03
pycharm安裝django框架詳細(xì)圖文教程(指定版本)
這篇文章主要給大家介紹了關(guān)于pycharm安裝django框架(指定版本)的相關(guān)資料,PyCharm是一種Python?IDE,帶有一整套可以幫助用戶在使用Python語言開發(fā)時提高其效率的工具,需要的朋友可以參考下2023-10-10
基于Python實現(xiàn)智能聊天機(jī)器人的完整代碼
本文基于Python語言搭建一款輕量級本地智能聊天機(jī)器人,無需依賴第三方付費接口,依靠基礎(chǔ)自然語言匹配邏輯實現(xiàn)人機(jī)對話功能,案例代碼簡潔易懂、可直接運行,需要的朋友可以參考下2026-05-05
Python中使用select模塊實現(xiàn)非阻塞的IO
這篇文章主要介紹了Python中使用select模塊實現(xiàn)非阻塞的IO,本文使用一個簡單聊天室程序講解Python中的select模塊使用,需要的朋友可以參考下2015-02-02
python 實現(xiàn)多線程的三種方法總結(jié)
這篇文章主要介紹了python 實現(xiàn)多線程的三種方法總結(jié),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04
python實現(xiàn)精準(zhǔn)搜索并提取網(wǎng)頁核心內(nèi)容
這篇文章主要為大家介紹了python實現(xiàn)精準(zhǔn)搜索并提取網(wǎng)頁核心內(nèi)容的實現(xiàn),有需要的的朋友可以借鑒參考下,希望能有所幫助祝大家多多進(jìn)步2021-11-11

