运行时可检查协议联合类型别名触发mypy实例检查错误问题
问题:Runtime-Checkable Protocol联合类型别名触发mypy泛型错误
问题场景
用户定义了一个联合类型别名_ConvertibleToInt,将SupportsInt、SupportsIndex和自定义的@runtime_checkable修饰的SupportsTrunc Protocol合并:
from typing import Protocol, runtime_checkable, SupportsInt, SupportsIndex @runtime_checkable class SupportsTrunc(Protocol): def __trunc__(self) -> int: ... _ConvertibleToInt = SupportsInt | SupportsIndex | SupportsTrunc
但执行isinstance(o, _ConvertibleToInt)检查时,mypy触发错误:
Parameterized generics cannot be used with class or instance checks
但以下两种场景无错误:
- 直接在
isinstance中写完整联合类型:isinstance(o, SupportsInt | SupportsIndex) - 仅给单个Protocol起别名,比如
MyProto = SupportsInt后执行isinstance(o, MyProto)
另外,重复同一Protocol的联合别名(如SupportsInt | SupportsInt)也会触发相同错误。用户确认所有Protocol均为@runtime_checkable,并非参数化泛型。
原因分析
这是mypy的已知bug,问题出在联合类型别名的处理逻辑漏洞:
- 当联合类型别名包含多个
@runtime_checkable的Protocol时,mypy会错误地将其识别为参数化泛型(实际并非如此) - 重复同一Protocol的联合也会触发bug,因为mypy处理联合类型去重时逻辑出错,误判为泛型类型
临时解决办法
- 直接写完整联合类型:跳过别名,直接在
isinstance中使用isinstance(o, SupportsInt | SupportsIndex | SupportsTrunc) - 合并为新的Runtime-Checkable Protocol:
@runtime_checkable class ConvertibleToInt(Protocol): def __int__(self) -> int: ... def __index__(self) -> int: ... def __trunc__(self) -> int: ...
(注:此方式要求实例同时实现三个方法,若需要“满足任意一个即可”的逻辑,建议用下方TypeGuard方案)
3. 用TypeGuard自定义检查函数:
from typing import TypeGuard def is_convertible_to_int(o: object) -> TypeGuard[SupportsInt | SupportsIndex | SupportsTrunc]: return isinstance(o, (SupportsInt, SupportsIndex, SupportsTrunc))
之后用if is_convertible_to_int(o):替代原isinstance检查,mypy可正确识别类型。
内容的提问来源于stack exchange,提问作者InSync
相关产品推荐
相关产品推荐

