如何在Protocol上定义带有协变返回类型的可调用属性?
如何在Protocol中正确定义协变的可调用返回类型?
你遇到的这个问题其实是Mypy对Protocol泛型变量的规则限制导致的——普通泛型类(比如你写的Works dataclass)里,协变类型变量用于可调用返回类型完全没问题,但换到Protocol里直接用就会报错,原因在于:
Mypy默认会把Protocol中定义的普通属性视为可读可写的。而对于可写属性来说,协变类型变量是不安全的(因为你可能会把父类型的值赋值给子类型属性),所以它会强制要求类型变量是不变的。但实际上你只是想把这个属性当作只读的来用,就像dataclass里的情况一样。
解决方法很简单:把Protocol里的可调用属性标记为只读属性,用@property装饰器就能做到。这样Mypy会意识到这个属性只能被读取,协变是安全的,从而允许你使用协变的类型变量R。
修正后的代码如下:
from typing import TypeVar, Callable, Protocol R = TypeVar("R", covariant=True) class WorksNow(Protocol[R]): @property def attribute(self) -> Callable[[], R]: ...
这样定义之后,你就可以像之前的dataclass一样正常使用协变特性了。比如下面的代码Mypy会完全认可:
from typing import Sequence class Impl: @property def attribute(self) -> Callable[[], list[int]]: return lambda: [1, 2, 3] # 合法:list[int]是Sequence[int]的子类型,R是协变的 w: WorksNow[Sequence[int]] = Impl()
本质上,这个操作是告诉Mypy:我们只会从这个属性读取值,不会修改它,所以协变的类型变量是安全的,不需要强制要求不变性。
内容的提问来源于stack exchange,提问作者MisterMiyagi
相关产品推荐
相关产品推荐

