如何让 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 中识别类的协议成员资格?的主要内容,如果未能解决你的问题,请参考以下文章

如何让 mypy 抱怨将 Any 分配给 int(第 2 部分)

将值列表映射到浮点数时出现mypy错误

mypy 无法识别从超类继承的变量

mypy 是不是有子类可接受的返回类型?

如何让 VS Code 智能感知识别附加到现有类的新方法

如何在PyCharm中运行mypy插件以检查键入的代码?