谈谈Python中的接口与抽象基类

接触Python比较早的朋友可能都有这样的体会,Python语言虽然也支持面向对象的编程方式,

但是,不像那些纯面向对象的语言(比如Java.NET)那样严格和规范。

随着项目的规模逐步扩大之后,想要以一种清晰、可维护和可扩展的方式定义和实施对象的行为就变得越来越困难。

今天介绍的Python中两个为面向对象编程提供的强大工具:接口抽象基类

它们的英文分别为ProtocolsABC(Abstract Base Classes)。

ProtocolsPython3.8才开始引入的,有的地方也翻译成协议 ,我感觉翻译成接口更熟悉一些。

ABC引入的比较早,在Python3之后得到了改进和优化,现在和其他语言的抽象类相比,差别不大。

1. 接口(Protocols)

Python3.8开始在类型模块中引入的接口Protocols的概念,它提供了一种无需显式继承即可定义接口的方法。

接口Protocols定义了一组方法或属性,只要一个对象实现了这些方法或属性,就被视为满足该接口。

下面通过一个示例来帮助理解Protocols的使用,如果有面向对象编程的经验,很容易就能理解这个概念。

这个示例来自最近用的一个量化交易系统的一部分,这个功能需要从不同的来源获取数据 ,然后进行分析 ,最后将分析结果以不同的方式输出

这三个步骤(获取数据,分析和输出)中,

假设获取数据 的来源有网络(API),文件(CSV)和数据库3种;

分析的步骤是统一的;输出的方式假设也有多种,比如邮件,短信等等。

根据这个描述,使用Protocols构建的获取数据和分析部分的代码如下:

输出的部分暂时不管

python 复制代码
from typing import Protocol


# 输入数据的接口
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"


# 分析数据
def analysis(i: InputData):
    data = i.get_data()

    print("开始处理数据...")

InputData继承了Protocol,其中定义了接口的函数get_data

只要实现了get_dataclass,比如APIHandlerCSVHandlerSqliteHandler,都可以当做InputData类型。

从代码可以看出,我们不需要用APIHandler去继承InputData,只要实现InputData中的所有方法就可以了。

这种灵活性确保了系统的可扩展性,我们可以添加新的数据源类型,而无需修改现有代码。

接下来我们测试上面的代码是否可以正常使用:

python 复制代码
if __name__ == "__main__":
    i = APIHandler()
    analysis(i)
    print("\n")

    i = CSVHandler()
    analysis(i)
    print("\n")

    i = SqliteHandler()
    analysis(i)
    print("\n")

运行结果:

bash 复制代码
$  python.exe .\protocol_abc.py
get_data from API
开始处理数据...


get_data from CSV
开始处理数据...


get_data from SQLITE DATABASE
开始处理数据...

2. 抽象基类(ABC)

Protocol非常具有灵活性,但有时我们需要更结构化的方法,这就是抽象基类 (ABC) 的用武之地。

ABC 是一种通过定义子类必须实现的严格接口来强制执行一致行为的工具。

Protocol不同,ABC 需要显式继承,因此当我们希望在代码中明确定义层次结构时,ABC是更好的选择。

接着实现上一节示例中的输出部分,每种不同的输出需要不同的配置,

比如输出到邮件需要先配置邮箱的账号信息,输出到短信需要配置手机信息等等。

在这里,我们使用 ABC 来实现输出的基类。

python 复制代码
# 输出的抽象基类
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

这里使用抽象基类的原因是输出时,并不是简单的调用send方法就可以的,还需要配置输出的参数,

所以用带有结构的抽象基类更好。

加上输出之后,上一节中的分析函数也改为:

python 复制代码
# 分析数据
def analysis(i: InputData, o: OutputResult):
    data = i.get_data()

    print("开始处理数据...")
    data = data.replace("get data from ", "")

    o.send(data)

测试的代码如下:

python 复制代码
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")

运行结果:

bash 复制代码
$  python.exe .\protocol_abc.py
get_data from API
开始处理数据...
send API to aaa@bbb.com


get_data from CSV
开始处理数据...
send CSV to 13911123456


get_data from SQLITE DATABASE
开始处理数据...
send SQLITE DATABASE to xyz@www.com

3. 两者的选择

当我们在实际的开发设计中,应该如何选择ProtocolABC呢?

其实,ProtocolABC之间的选择并不是非黑即白,这通常取决于项目的背景和你的目标。

一般来说,下面这些情况我们优先选择使用Protocol

  • 你正在使用现有代码或希望集成第三方库
  • 灵活性是首要任务,你不想强制执行严格的层次结构
  • 来自不相关的类层次结构的对象需要共享行为

而下面这些情况,优先选择ABC

  • 正在从头开始设计一个系统,并且需要加强结构
  • 类之间的关系是可预测的,并且继承是有意义的
  • 共享功能或默认行为可以减少重复并提高一致性

4. 总结

总的来说,ProtocolABC不是互相竞争的两种工具,它们是互补的。

我使用Protocol将类型安全改造到遗留系统中,而不需要大量重构。

另一方面,如果我在从头开始构建一个结构和一致性至关重要的系统时,会使用 ABC

在决定使用哪个时,请考虑项目的灵活性需求和长期目标。

Protocol提供灵活性和无缝集成,而 ABC 有助于建立结构和一致性。

通过了解它们各自的优势,你可以选择合适的方式来构建健壮、可维护的 Python 系统。

相关推荐
水木流年追梦几秒前
大模型入门-Reward 奖励模型训练
开发语言·python·算法·leetcode·正则表达式
JavaWeb学起来几秒前
Python学习教程(六)数据结构List(列表)
数据结构·python·python基础·python教程
liuyunshengsir13 分钟前
PyTorch 动态量化(Dynamic Quantization)
人工智能·pytorch·python
电子云与长程纠缠22 分钟前
UE5制作六边形包裹球体效果
开发语言·python·ue5
DFT计算杂谈31 分钟前
KPROJ编译教程
java·前端·python·算法·conda
念恒123061 小时前
Python(循环中断)
开发语言·python
tsfy20031 小时前
Python 处理中文文件名的3个坑(附 Flask 上传解决函数)
开发语言·python·flask·文件上传·中文编码
AI技术控1 小时前
KV Cache 缓存机制的原理和应用:从 Transformer 推理到大模型服务优化
人工智能·python·深度学习·缓存·自然语言处理·transformer
vx-程序开发2 小时前
基于机器学习的动漫可视化系统的设计与实现-计算机毕业设计源码08339
java·c++·spring boot·python·spring·django·php
爱睡懒觉的焦糖玛奇朵2 小时前
【从视频到数据集:焦糖玛奇朵的魔法工具Video To YOLO Dataset】
人工智能·python·学习·yolo·音视频