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在这里是正确的!
您可以对协议进行不同的定义,只需要具有两个类型为MyObj
和str
的位置参数的可调用
class Proto(Protocol):
def __call__(self, obj: MyObj, name: str, /) -> None:
pass
现在您不能使用命名参数,因此该方法和我的incompatible
函数与协议兼容。
有趣的是,如果我删除;name";参数,则类型检查通过。
我无法重现。它应该会失败,而且会