如何解决如何在协议上定义具有协变返回类型的可调用属性?
通常理解为可调用的返回类型是covariant。当使用可调用的属性定义类型时,我确实可以使返回类型通用和协变:
from typing import TypeVar,Callable,Generic,Sequence
from dataclasses import dataclass
R = TypeVar("R",covariant=True)
@dataclass
class Works(Generic[R]):
call: Callable[[],R] # returns an R *or subtype*
w: Works[Sequence] = Works(lambda: []) # okay: list is subtype of Sequence
然而,这同样不适用于 Protocol
。当我以同样的方式为该类型定义一个 Protocol
时,MyPy 拒绝这样做——它坚持返回类型必须是 invariant。
from typing import TypeVar,Protocol
R = TypeVar("R",covariant=True)
class Fails(Protocol[R]):
attribute: Callable[[],R]
$ python -m mypy so_testbed.py --pretty
so_testbed.py:5: error: Covariant type variable "R" used in protocol where invariant one is expected
class Fails(Protocol[R]):
^
Found 1 error in 1 file (checked 1 source file)
如何为尊重 Protocol
协方差的具体类型正确定义 R
?
解决方法
使用 Protocol
显然无法实现您的尝试 - 请参阅 PEP 544 中的以下内容:
可变属性的协变子类型
被拒绝,因为协变 可变属性的子类型化是不安全的。考虑这个例子:
class P(Protocol):
x: float
def f(arg: P) -> None:
arg.x = 0.42
class C:
x: int
c = C()
f(c) # Would typecheck if covariant subtyping
# of mutable attributes were allowed.
c.x >> 1 # But this fails at runtime
最初提议出于实际原因允许这样做,但它 随后被拒绝,因为这可能会掩盖一些难以发现的错误。
由于您的 attribute
是可变成员 - 您不能让它与 R
协变。
一种可能的替代方法是用一个方法替换 attribute
:
class Passes(Protocol[R]):
@property
def attribute(self) -> Callable[[],R]:
pass
它通过了类型检查 - 但它是一个不灵活的解决方案。
如果您需要可变协变成员,则 Protocol
不是最佳选择。
正如@Daniel Kleinstein 指出的那样,您不能通过协变变量来参数化协议类型,因为它用于可变属性。
另一种选择是将变量分成两个(协变和不变)并在两个协议中使用它们(replace Callable
和 Protocol
)。
from typing import TypeVar,Callable,Protocol
R_cov = TypeVar("R_cov",covariant=True)
R_inv = TypeVar("R_inv")
class CallProto(Protocol[R_cov]):
def __call__(self) -> R_cov: ...
class Fails(Protocol[R_inv]):
attribute: CallProto[R_inv]
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。