You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 22:47:52