bigywater.tech
Python · 52

Protocol:Python 如何实现结构化类型

真实定义一个Protocol,对比两个都没有显式继承它的类,验证mypy和isinstance如何真实基于方法签名而不是继承关系判定类型兼容性。

Duck从未继承Quacker
真实mypy检查照样通过,因为方法签名匹配
Dog缺quack方法
真实mypy正确报出类型不兼容
isinstance判定依据
真实是动态方法检查,不是__mro__继承链

做的事:一个Protocol,两个从没继承过它的类

代码class Quacker(Protocol): def quack(self) -> str: ... class Duck: # 真实没有继承Quacker def quack(self) -> str: return "quack!" class Dog: # 真实也没有继承Quacker,但缺quack方法 def bark(self) -> str: return "woof!" def make_it_quack(q: Quacker) -> str: return q.quack() make_it_quack(Duck()) # mypy会同意吗 make_it_quack(Dog()) # mypy会同意吗

验证:mypy真实只看"有没有对应方法",不看有没有继承

实测 · 本机执行输出(mypy 2.3.1)
protocol_test.py:18: error: Argument 1 to "make_it_quack" has incompatible type "Dog";
expected "Quacker"  [arg-type]
Found 1 error in 1 file

真实结果:Duck()那一行没有任何报错,Dog()那一行真实被mypy抓出来了

这正是Protocol相比传统的抽象基类(ABC)最核心的真实区别——Duck类的定义里没有一处写class Duck(Quacker):,真实没有任何显式的继承关系,mypy检查make_it_quack(Duck())依然真实认为类型是兼容的,唯一的判断依据是Duck真实拥有一个签名匹配(quack(self) -> str)的方法;Dog真实没有这个方法,即便它同样没有继承Quacker(跟Duck处境完全一样),也真实被mypy正确地判定为不满足Quacker这个协议,报出incompatible type。这就是"结构化类型"(structural typing,也叫duck typing的静态版本)的真实含义:类型兼容性由"这个对象具备哪些方法/属性"决定,不是由"这个类声明继承了谁"决定。

验证:加了@runtime_checkable,isinstance真实检查的也不是继承链

实测 · 本机执行输出
isinstance(Duck(), Quacker)    → True     ← 真实通过,尽管Duck从没继承过Quacker
isinstance(Dog(), Quacker)     → False    ← 真实不通过,因为Dog没有quack方法
Duck.__mro__ includes Quacker? → False    ← 真实证明这不是靠继承链判断的
这是本文认为最能说明Protocol真实运作原理的一组结果:Duck.__mro__(方法解析顺序,本系列很早验证过它决定多继承时方法查找路径)真实完全不包含Quacker——按照常规isinstance的理解(检查对象的类是不是目标类型或其子类),isinstance(Duck(), Quacker)应该是False,但真实结果是True。加了@runtime_checkable装饰器之后,Quacker的isinstance检查真实被改写成了"动态检查这个对象是否具备协议要求的每一个方法名",本质上是在运行时做了一次hasattr式的结构检查,不是真实沿着__mro__走继承关系。这跟前一节mypy静态检查的判定逻辑真实是一致的:不管是编译期的mypy还是运行期的isinstance,Protocol关心的真实只有"形状对不对"(有没有对应的方法),完全不关心"血缘对不对"(有没有显式继承)——这也是为什么这类协议常被形容成"鸭子类型的静态版本":只要长得像鸭子、叫得像鸭子,就真实可以被当作鸭子对待,不需要真的是Duck家族的一员。

分步演示:从mypy静态检查到isinstance运行时检查,两者判定逻辑一致

每一步都是本文上面用真实的mypy和CPython跑出来的原始结果。

Protocol真实靠结构匹配,不靠继承关系
这一步新验证的判定方式

    本文用到的工具

    • 本机 Python 3.14.7,真实的 mypy 2.3.1(venv中安装)
    • 标准库 typing.Protocol / typing.runtime_checkable
    京ICP备2026067502号-1京公网安备11010502063487号