PAYATHON 2026

Python 3.9 中 data Protocol 为何未装饰也能调用 isinstance?

数据小许

结论

需要用 isinstance() 在运行时检查 Protocol 时,应为协议添加 @runtime_checkable。Python 3.9 示例中的 AssertionError 表示 isinstance(B(), A) 返回了 False,检查没有通过。这个结果不能说明 data protocol 不需要装饰器。

为什么示例结果不同

A 声明了 x: int,因此 B 需要同时提供 x 和 f 才符合该协议。示例中的 B 只有 f,缺少 x。运行时结构检查返回 False 时,assert 就会抛出 AssertionError。

AssertionError 和 TypeError 表示的情况不同:前者表示表达式执行后得到假值;后者表示当前协议不允许用于这种运行时检查。给出的 Python 3.9 和 Python 3.10+ 输出表明,未装饰时的具体行为会因版本而异。仅凭这些输出无法确定 Python 3.9 内部为什么返回了假值,也不能把这种行为当作接口保证。

推荐写法

如果需要在运行时用 isinstance() 检查协议,请显式添加 @runtime_checkable,并确保实例实现协议要求的成员:

from typing import Protocol, runtime_checkable

@runtime_checkable
class A(Protocol):
    x: int
    def f(self): ...

class B:
    x = 1

    def f(self):
        ...

assert isinstance(B(), A)

运行时检查主要确认对象是否具备协议要求的成员。它不会验证成员的类型注解是否匹配,也不会检查方法签名是否符合静态类型要求。需要完整检查类型兼容性时,应使用静态类型检查工具。

注意事项

@runtime_checkable 允许对协议使用 isinstance(),但缺少成员的对象仍不会通过检查。对于包含数据成员的协议,也不要用 issubclass() 替代:类级别检查无法确认实例上是否存在数据属性。跨 Python 版本使用这类协议检查时,请显式装饰协议,并根据实际需要检查实例成员。

备注:内容仅供参考。