如何让 Mypy 在 Callable 中识别类的协议成员资格?
Posted
技术标签:
【中文标题】如何让 Mypy 在 Callable 中识别类的协议成员资格?【英文标题】:How to get Mypy to recognize a class's protocol membership within a Callable? 【发布时间】:2020-04-24 16:07:49 【问题描述】:当协议被用作类型注释函数的简单参数时,Mypy 可以正确识别类对协议的遵守情况。但是,当我有一个需要使用该协议的可调用参数的函数时,Mypy 会错过用户类的协议成员资格。
我是在滥用 Mypy 的协议模式,还是 Mypy 目前根本不支持这种模式?
(我已经看到有关 Mypy 遇到 Callables 问题的帖子that get assigned to a class.. 所以这可能是已知行为)
from typing_extensions import Protocol
from typing import Callable
class P(Protocol) :
def foo(self) -> None : ...
def requires_P(protocol_member : P) -> None :
protocol_member.foo()
def requires_P_callable(protocol_member : P, function: Callable[[P],None]) -> None :
function(protocol_member)
class C :
def foo(self) :
print("bar")
if __name__ == '__main__' :
c = C()
def call_foo(c: C) -> None:
c.foo()
requires_P(c)
# mypy is fine with this
requires_P_callable(c, call_foo)
# mypy complains :
# Argument 2 to "requires_P_callable" has incompatible type "Callable[[C], None]"; expected "Callable[[P], None]"
【问题讨论】:
它看起来像mypy
中的一个错误
我不确定:python.org/dev/peps/pep-0483/#covariance-and-contravariance 请阅读有关 Callables 的协变和逆变。
谢谢安德鲁。我不知道 Callables 中参数的逆变性,所以这对我非常有帮助。尽管在我的示例中 C 严格来说不是 P 的子类型,但我想 co/contra -variance 与 Mypy 如何检查协议模式有关?
【参考方案1】:
如果将 call_foo 的定义替换为:
def call_foo(c: P) -> None:
c.foo()
错误消失,程序继续工作...如果停止使用Protocol并让C成为P的孩子,情况也是如此。
第二种解决方法是:
from typing import Callable, Protocol, TypeVar
_TP = TypeVar('_TP', bound='P')
class P(Protocol):
def foo(self) -> None:
...
class C:
def foo(self) -> None:
print("foo")
def requires_P_callable(prot: _TP, func: Callable[[_TP], None]) -> None:
func(prot)
def call_foo(c: C) -> None:
c.foo()
if __name__ == '__main__':
c = C()
requires_P_callable(c, call_foo)
【讨论】:
非常感谢您提供的所有解决方案。 TypeVar 方法对我来说效果很好。我犹豫要不要使用def call_foo(c: P)
[...] ,因为我的项目中每个实现 P
的类都有不同的 call_foo
变体。现在我有点了解导致这种情况的原因,我觉得我可以在各种解决方法中做出更明智的选择。以上是关于如何让 Mypy 在 Callable 中识别类的协议成员资格?的主要内容,如果未能解决你的问题,请参考以下文章