https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce 是Python中用于静态类型检查的轻量级鸭子类型契约,不介入运行时,零开销,仅在mypy等工具中生效;它不要求继承,只要结构匹配即视为实现,不可实例化,与ABC有本质区别。

https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce 是用来替代抽象基类的轻量契约
Python 的 https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce 不是运行时强制的接口,而是一种类型检查层面的“鸭子类型契约”。它不参与继承、不生成元类、不修改 mro,只在 mypy 或 IDE 类型推导时起作用。你写一个类只要“看起来像”某个 https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce(即有对应方法签名),它就被认为是该协议的实现者。
- 常见错误现象:
TypeError: Cannot instantiate abstract class—— 这说明你误把https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce当成ABC用了,https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce本身不能被实例化,但它的子类可以(只要满足结构) - 使用场景:想表达“只要支持
.read()和.close()就算文件类对象”,又不想让所有实现都继承自某个基类 - 参数差异:
class Readable(https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce): def read(self, size: int = -1) -> bytes: ...中的默认参数会被类型检查器识别,但运行时不校验;如果漏写= -1,mypy可能报错说“调用不匹配” - 性能影响:零运行时开销。没导入
typing.https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce的话,连类型提示都不会存在
https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce 和 ABC 的关键区别在运行时行为
ABC 是运行时强制的抽象机制,靠 @abstractmethod 和 subclasshook 实现;https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce 完全不介入运行时,只服务静态类型系统。
- 常见错误现象:给
https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce加@abstractmethod—— 没用,也不会报错,但会让类型检查器困惑(mypy会忽略该装饰器) - 使用场景:需要动态判断某个对象是否“符合协议”?别用
isinstance(obj, Myhttps://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce),它永远返回False;改用hasattr(obj, 'method_name')或第三方库如typing_extensions.runtime_checkable(加了这个装饰器后才支持isinstance) - 兼容性影响:Python 3.8+ 原生支持
https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce,但runtime_checkable在 3.8–3.11 需要从typing_extensions导入;3.12+ 才把runtime_checkable移入标准库typing - 示例:
from typing import https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce
class Serializer(https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce): def dumps(self, obj) -> bytes: ... def loads(self, data: bytes): ...
下面这个类没继承任何东西,但 mypy 会认为它符合 Serializer
class JSONSerializer: def dumps(self, obj) -> bytes: return json.dumps(obj).encode() def loads(self, data: bytes): return json.loads(data.decode())
嵌套 https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce 和泛型组合容易出类型擦除问题
https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce 支持泛型(比如 class MapperT, U: ...),但和普通泛型类不同:它不保留类型参数到运行时,也不做协变/逆变推导,除非显式声明。
- 常见错误现象:
mypy报Argument 1 has incompatible type "int"; expected "str",但实际代码没问题——往往是因为泛型https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce缺少call或方法签名未完整覆盖类型变量约束 - 使用场景:定义一个可调用协议,比如
Callable[[T], U]的结构等价物,但希望带额外属性(如.name) - 性能 / 兼容性影响:泛型
https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce在mypy0.950+ 才较稳定;旧版本可能跳过检查或误报;Python 3.12 开始支持更自然的泛型语法(如class PT: ...),但老写法class P(Generic[T], https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce): ...仍需保留兼容性 - 关键点:如果协议里用到了类型变量,必须在至少一个方法签名中“使用”它(不能只出现在注释或字符串里),否则
mypy会当作非泛型处理
https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce 不解决运行时适配,别指望它自动转换行为
有人以为定义了 class SupportsAdd(https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce): def add(self, other): ...,就能让两个不相关的类“自动支持 + 操作”——不行。https://www.php.cn/link/82aa20a74dbe3e0e85c23ba8d645d3ce 不注入方法,不修改 add 查找逻辑,也不触发任何魔法。
立即学习“Python免费学习笔记(深入)”;
- 常见错误现象:写了
def process(x: SupportsAdd) -> SupportsAdd:,结果传入str和list时运行时报TypeError: unsupported operand type(s)—— 类型检查通过了,但运行时语义不匹配 - 使用场景:适合描述已有行为的一致性(比如多个数据库驱动都有
.execute()),不适合填补行为空缺 - 容易被忽略的地方:协议只看“有没有同名方法”,不看“方法是否真能处理你的参数”。
len协议成立,不代表len(x)不会抛ValueError;协议成立只是第一步,真实健壮性还得靠测试和文档
事情说清了就结束










