Python 现代开发学习笔记

(十一)用抽象基类与协议写清接口

1. 抽象基类要求子类完成指定操作

开发一组同类来源时,可以显式给出一个基类,并把必须实现的方法标成抽象方法:

# ABC 建立抽象基类,abstractmethod 标出需要由具体类完成的方法。
from abc import ABC, abstractmethod


class TitleSource(ABC):
    @abstractmethod
    def load(self) -> list[str]:
        raise NotImplementedError


class MemorySource(TitleSource):
    def load(self) -> list[str]:
        return ["Python", "NumPy"]


try:
    TitleSource()
except TypeError:
    print("load 尚未实现")
print(MemorySource().load())

输出“load 尚未实现”和 ['Python', 'NumPy']。抽象方法仍未完成的类,在创建实例时就会被报告为错误。NotImplementedError 在方法体中表示这项处理尚未提供;这里对抽象类的创建检查发生得更早,不会先进入这个方法体。

抽象基类可以同时提供公共实现与抽象接口;继承它的具体类完成抽象方法后,再用公共处理。抽象标记要求实现存在,参数与返回数据的具体含义仍由接口说明和实际调用共同核对。抽象基类

2. Protocol 描述结构,不强制继承同一父类

上一章的类型注解也能描述“这个对象提供哪些方法”。下面的协议写明来源提供 load() -> list[str]:

# Protocol 让类型工具按对象提供的方法结构检查协作关系。
from typing import Protocol


class TitleSource(Protocol):
    def load(self) -> list[str]:
        ...  # 省略号在这里作为接口方法体的占位,不提供实际读取行为。


class MemorySource:
    def load(self) -> list[str]:
        return ["Python", "NumPy"]


def total_titles(source: TitleSource) -> int:
    return len(source.load())


print(total_titles(MemorySource()))

输出 2。MemorySource 没有继承 TitleSource,但它的方法形状符合这份协议。普通 Python 运行仍然按 source.load() 调用;协议注解供编辑器与类型检查工具分析,不会自动替函数检查所有输入与返回结果。结构化协议

安排方式 这份示例中明确表达的内容
直接使用鸭子类型 调用者使用 load(),由实际对象提供实现
抽象基类 明确建立继承关系,抽象方法未完成时阻止实例化
Protocol 在注解中写出调用者需要的方法结构

综合书架使用协议描述存储器,再通过组合传入具体实现:接口形状与状态所在的位置分别写清楚。

笔记正文与全部题解 · Ctrl / ⌘ K