如何让 Mypy 在 Callable 中识别 class 的协议成员资格?

How to get Mypy to recognize a class's protocol membership within a Callable?

当协议用作类型注释函数的简单参数时,Mypy 正确识别 class 对协议的遵守。但是,当我有一个函数需要使用该协议的可调用参数时,Mypy 会错过用户 class 的协议成员资格。

我是在滥用 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]"



如果将 call_foo 的定义替换为:

def call_foo(c: P) -> None: c.foo()

错误消失,程序继续工作...如果停止使用协议并使 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)