无法让 mypy 识别变量实现协议
Having trouble getting mypy to recognize that variable implements a protocol
当 运行 与 mypy --strict
时,以下代码会产生错误
from typing import Protocol
class Proto(Protocol):
id: int
name: str
def work(self, hours: float) -> str: ...
class RoleA:
def work(self, hours: float) -> str:
return f"A {hours}"
class RoleB:
def work(self, hours: float) -> str:
return f"B {hours}"
class X:
def __init__(self, id: int, name: str) -> None:
self.id = id
self.name = name
class Y(X, RoleA):
def __init__(self, id: int, name: str) -> None:
super().__init__(id, name)
class Z(X, RoleB):
def __init__(self, id: int, name: str) -> None:
super().__init__(id, name)
def track(items: list[Proto], hours: float) -> None:
for item in items:
result = item.work(hours)
if __name__ == "__main__":
y = Y(id = 4, name = "Jane Doe")
z = Z(id = 3, name = "Kevin Bacon")
items = [y, z]
track(items, 40)
错误如下:
program.py:38: error: Argument 1 to "track" has incompatible type "List[X]"; expected "List[Proto]"
我不明白为什么会这样,据我所知 Y
和 Z
都实现了 Proto
协议,那么为什么 mypy
能够推断出它们是 track
函数的有效参数吗?
每个变量在声明时都会绑定一个特定的类型。对于没有注释的 non-generic 类型,它只是赋值右侧的对象类型,这几乎总是正确的(除非您打算稍后将其重新分配给不同的类型,在这种情况下您需要事先将其声明为联合,以便它可以接受任何一种类型)。
当您没有显式声明泛型类型(如列表或其他集合)的类型参数时:
items = [y, z]
mypy 对此做出了最好的猜测。在本例中是 List[X]
,因为 X
是 y
和 z
中最明显的常见类型。绝大多数情况下推断的类型都是您想要的类型,但有时它要么太宽泛要么太狭窄,在这种情况下您可以明确指定一个:
items: list[Proto] = [y, z] # or typing.List[Proto] for python <3.10
这将修复您的 track
调用中的错误。
通常我认为 mypy 不会将 Protocol
类型推断为多个对象的通用类型(因为给定对象可能实现任意数量的协议),因此您需要声明它明确地以一种或另一种方式。
当 运行 与 mypy --strict
from typing import Protocol
class Proto(Protocol):
id: int
name: str
def work(self, hours: float) -> str: ...
class RoleA:
def work(self, hours: float) -> str:
return f"A {hours}"
class RoleB:
def work(self, hours: float) -> str:
return f"B {hours}"
class X:
def __init__(self, id: int, name: str) -> None:
self.id = id
self.name = name
class Y(X, RoleA):
def __init__(self, id: int, name: str) -> None:
super().__init__(id, name)
class Z(X, RoleB):
def __init__(self, id: int, name: str) -> None:
super().__init__(id, name)
def track(items: list[Proto], hours: float) -> None:
for item in items:
result = item.work(hours)
if __name__ == "__main__":
y = Y(id = 4, name = "Jane Doe")
z = Z(id = 3, name = "Kevin Bacon")
items = [y, z]
track(items, 40)
错误如下:
program.py:38: error: Argument 1 to "track" has incompatible type "List[X]"; expected "List[Proto]"
我不明白为什么会这样,据我所知 Y
和 Z
都实现了 Proto
协议,那么为什么 mypy
能够推断出它们是 track
函数的有效参数吗?
每个变量在声明时都会绑定一个特定的类型。对于没有注释的 non-generic 类型,它只是赋值右侧的对象类型,这几乎总是正确的(除非您打算稍后将其重新分配给不同的类型,在这种情况下您需要事先将其声明为联合,以便它可以接受任何一种类型)。
当您没有显式声明泛型类型(如列表或其他集合)的类型参数时:
items = [y, z]
mypy 对此做出了最好的猜测。在本例中是 List[X]
,因为 X
是 y
和 z
中最明显的常见类型。绝大多数情况下推断的类型都是您想要的类型,但有时它要么太宽泛要么太狭窄,在这种情况下您可以明确指定一个:
items: list[Proto] = [y, z] # or typing.List[Proto] for python <3.10
这将修复您的 track
调用中的错误。
通常我认为 mypy 不会将 Protocol
类型推断为多个对象的通用类型(因为给定对象可能实现任意数量的协议),因此您需要声明它明确地以一种或另一种方式。