为何list无法适配逆变的Settable协议?mypy与pyright报错疑问
为何list无法适配逆变的Settable协议?mypy与pyright报错疑问
这个问题的核心是你对逆变的方向和list的类型推断存在两个小误解,我们一步步拆解:
1. 你的list被推断为list[None],而非接受任意类型的list
当你写l = [None, None]时,类型检查器会自动根据初始元素推断出l的类型是list[None]——也就是说,这个列表的元素只能是None,它的__setitem__方法签名实际上是:
def __setitem__(self, idx: int, value: None) -> None
这和你以为的“list的__setitem__接受Any”完全不同,只有当你显式注解类型时,才能让它接受任意类型的值。
2. 你搞反了逆变的方向
你提到“逆变意味着list的__setitem__是__setitem__(idx: int, obj: str)的实例”,这里刚好搞反了函数参数逆变的逻辑:
- 函数参数的逆变是指:如果协议要求方法接受类型A作为参数,那么只有那些接受A的超类型(更宽泛的类型)的方法才符合要求。比如,接受
object的方法可以兼容要求接受str的协议,因为str是object的子类,任何str实例都能传给接受object的参数。 - 反过来,如果一个方法只能接受A的子类型(更窄的类型,比如
None),那它根本无法处理A类型的实例(比如你不能把str传给只能接受None的方法),自然不符合协议要求。
3. 回到你的案例:为什么list[None]不兼容Settable[int, str]
你的Settable[int, str]协议要求__setitem__能接受(int, str)类型的参数,但list[None]的__setitem__只能接受(int, None)——None是str的子类型(而非超类型),完全无法处理str类型的值,所以类型检查器会判定它不兼容协议。
解决方法
如果你想要让l能被传入hw,只需要显式注解它的类型为接受str的列表即可:
# 方法1:允许任意类型(简单直接) l: list[Any] = [None, None] hw(l) # 方法2:更精确的联合类型(推荐,保留类型安全) l: list[str | None] = [None, None] hw(l)
这两种情况下,list的__setitem__会被类型检查器认为可以接受str类型的值,符合逆变的要求。
mypy和pyright的行为完全符合PEP 484(Python类型提示规范),它们严格遵循了函数参数的逆变规则和泛型类型的推断逻辑。
备注:内容来源于stack exchange,提问作者0x60
相关产品推荐
相关产品推荐

