Proto
,然后定义一个采用该协议的变量的函数。然后定义类
A
和
B
,我认为它们都遵循协议,尽管只有
B.__call__
的参数名称与协议不同(在
Proto
中它是
x
,在
B
中它是
y
) ).通过mypy检查以下代码后,出现以下错误
main.py:20: error: Argument 1 to "func" has incompatible type "B"; expected "Proto"
看来,协议不仅强制类型,还强制参数名称。这是有意的行为吗?或者 mypy 有什么问题吗?
from typing import Protocol
class Proto(Protocol):
def __call__(self, x: int) -> int:
...
def func(f: Proto):
pass
class A:
def __call__(self, x: int) -> int:
return x
class B:
def __call__(self, y: int) -> int:
return y
func(A())
func(B())
Proto
p
称为
p(0)
或
p(x=0)
。
B
不满足第二个。如果您希望
B
有效,您可以强制使用位置参数
class Proto(Protocol):
def __call__(self, x: int, /) -> int:
...
注意:这个答案是为那些需要向后兼容性的人提供的:对于 python 3.8 及更高版本,请使用仅位置参数声明。
或者,您可以使用__dunder_beginning
名称来表示“我不关心该名称 - 允许任何名称”。
mypy
支持它(文档这里),并且由 PEP484 支持。 Typeshed 使用此语法,因此所有类型检查器都应该支持它。这是向后兼容的(posonly 参数在 3.8 中引入,3.7 仍然存在)声明协议不尊重参数名称的方式。
# example from docs above
from typing import Callable, TypeVar
from typing_extensions import Protocol
T = TypeVar('T')
class Copy(Protocol):
def __call__(self, __origin: T) -> T: ...
copy_a: Callable[[T], T]
copy_b: Copy
copy_a = copy_b # OK
copy_b = copy_a # Also OK
# Your example
from typing import Protocol
class Proto(Protocol):
def __call__(self, __x: int) -> int: # Change here
...
def func(f: Proto):
pass
class A:
def __call__(self, x: int) -> int:
return x
class B:
def __call__(self, y: int) -> int:
return y
func(A())
func(B())
这通过了类型检查。
这不适用于*args
和
**kwargs
,因为无论如何他们的名字都不会被考虑在内。