如何正确定义不可变有序集合的结构化类型Protocol(无需自行实现完整Protocol)
嗨,我来帮你捋清楚这个问题~你现在的代码其实已经能实现你想要的静态类型检查效果了,但那个mypy警告确实是个合规性问题——PEP 544明确规定Protocol只能继承其他Protocol,而旧版本里的collections.abc.Set和Sequence确实不属于Protocol类型(虽然它们本身是只读的抽象接口)。
下面给你两种完全合规的解决方式,都不需要你手动实现完整的Protocol接口:
方法1:用typing_extensions.Protocol的专属参数(推荐兼容多版本)
typing_extensions里的Protocol提供了一个allow_explicit_abc参数,专门用来解决「想让Protocol继承抽象基类(ABC)」的场景。它能让mypy合法允许你继承Set和Sequence这类ABC,同时保留Protocol的结构类型检查特性,完全复用它们的现有接口定义。
修改后的代码如下:
from ordered_set import OrderedSet from typing import TypeVar from collections.abc import Set, Sequence from typing_extensions import Protocol T = TypeVar("T", covariant=True) # 通过allow_explicit_abc=True允许Protocol继承ABC类 class ConstOrderedSet(Protocol[T], Set[T], Sequence[T], allow_explicit_abc=True): pass x: ConstOrderedSet[int] = OrderedSet([1, 2, 3]) x.append(5) # 会被mypy正确标记为错误 x[1] # 正常通过检查 x[1] = 2 # 会被mypy正确标记为错误
这样既消除了mypy的misc警告,又完全不用自己手写任何额外的方法签名,完美符合你的需求。
方法2:升级Python和mypy版本(最简方案)
在Python 3.9及以后的版本中,PEP 585对标准库的抽象基类做了升级,collections.abc.Set和Sequence现在同时兼具ABC和Protocol的特性(也就是「兼容Protocol的ABC」)。如果你能升级到Python 3.9+,再搭配最新版的mypy,直接保留你原来的类定义,去掉# type: ignore[misc]注释就可以了——mypy会自动识别它们为合法的Protocol父类,不会再弹出警告。
为什么你的原始代码能正常工作?
其实mypy在处理结构类型检查时,会宽松地支持这种「不合规」的继承,因为它能识别出Set和Sequence的抽象接口完全符合结构子类型的要求。但从PEP规范的角度来说这确实不够严谨,上面两种方法就是把它变得完全合规的方式。
备注:内容来源于stack exchange,提问作者mcocdawc

