具有未绑定方法的回调协议


from typing import Protocol

class MyObj:
def my_method(self, name: str):
pass

class Proto(Protocol):
def __call__(self, obj: MyObj, name: str):
pass

def my_fn(obj: MyObj, name: str):
pass

def caller(fn: Proto):
fn(MyObj(), 'some name')

caller(my_fn)  # passes type check
caller(MyObj.my_method)  # fails type check

我使用mypy 0.971进行类型检查。据mypy说,我很难理解为什么第二个电话是非法的。根据Python静态类型规则,它真的不正确吗?

有趣的是,如果我去掉";name";参数,类型检查通过:

from typing import Protocol

class MyObj:
def my_method(self):
pass

class Proto(Protocol):
def __call__(self, obj: MyObj):
pass

def my_fn(obj: MyObj):
pass

def caller(fn: Proto):
fn(MyObj())

caller(my_fn)  # passes
caller(MyObj.my_method)  # passes

编辑:

根据@Wombatz的解释,如果我将协议修改为:

class Proto(Protocol):
def __call__(self, obj: MyObj, /, name: str):
pass

它起作用了,因为现在第一个参数的名称无关紧要因为它需要用位置参数来调用。

问题是该协议比您想象的更具限制性。

class Proto(Protocol):
def __call__(self, obj: MyObj, name: str) -> None:
pass

def incompatible(what: MyObj, name: str) -> None:
pass

函数incompatible也与该协议不兼容,因为该协议需要一个可调用函数,其中第一个参数是MyObj其名称为obj,第二个参数是str且其名称为name

因此,理论上,该协议可以这样使用:

def caller(p: Proto) -> None:
p(obj=MyObj(), name="Hello")

这对my_func有效,但对方法无效,因为方法的第一个参数的名称是self,而不是obj。所以mypy在这里是正确的!

您可以对协议进行不同的定义,只需要具有两个类型为MyObjstr位置参数的可调用

class Proto(Protocol):
def __call__(self, obj: MyObj, name: str, /) -> None:
pass

现在您不能使用命名参数,因此该方法和我的incompatible函数与协议兼容。

有趣的是,如果我删除;name";参数,则类型检查通过。

我无法重现。它应该会失败,而且会

最新更新