【发布时间】:2021-09-19 22:43:45
【问题描述】:
我正在尝试实现一个通用协议。我的意图是拥有一个带有简单 getter 的 Widget[key_type, value_type] 协议。 Mypy 抱怨Protocol[K, T],因此变成了Protocol[K_co, T_co]。我已经去除了所有其他限制,但我什至无法让最基本的情况widg0: Widget[Any, Any] = ActualWidget() 工作。 ActualWidget.get 应该与get(self, key: K) -> Any 完全兼容,这让我觉得我在某种程度上使用了泛型/协议错误,或者 mypy 无法处理。
来自 mypy 的命令/错误:
$ mypy cat_example.py
cat_example.py:34: error: Argument 1 to "takes_widget" has incompatible type "ActualWidget"; expected "Widget[Any, Any]"
cat_example.py:34: note: Following member(s) of "ActualWidget" have conflicts:
cat_example.py:34: note: Expected:
cat_example.py:34: note: def [K] get(self, key: K) -> Any
cat_example.py:34: note: Got:
cat_example.py:34: note: def get(self, key: str) -> Cat
Found 1 error in 1 file (checked 1 source file)
或者,如果我尝试使用 widg0: Widget[Any, Any] = ActualWidget() 强制分配:
error: Incompatible types in assignment (expression has type "ActualWidget", variable has type "Widget[Any, Any]")
完整代码:
from typing import Any, TypeVar
from typing_extensions import Protocol, runtime_checkable
K = TypeVar("K") # ID/Key Type
T = TypeVar("T") # General type
K_co = TypeVar("K_co", covariant=True) # ID/Key Type or subclass
T_co = TypeVar("T_co", covariant=True) # General type or subclass
K_contra = TypeVar("K_contra", contravariant=True) # ID/Key Type or supertype
T_contra = TypeVar("T_contra", contravariant=True) # General type or supertype
class Animal(object): ...
class Cat(Animal): ...
@runtime_checkable
class Widget(Protocol[K_co, T_co]):
def get(self, key: K) -> T_co: ...
class ActualWidget(object):
def get(self, key: str) -> Cat:
return Cat()
def takes_widget(widg: Widget):
return widg
if __name__ == '__main__':
widg0 = ActualWidget()
#widg0: Widget[str, Cat] = ActualWidget()
#widg0: Widget[Any, Any] = ActualWidget()
print(isinstance(widg0, Widget))
print(isinstance({}, Widget))
takes_widget(widg0)
【问题讨论】:
-
我认为您需要做的就是将 Widget 协议中的 typehint 更改为
str。没有? -
不,小部件是通用的。
-
我不认为更改
key的类型会使 Widget 成为非通用的。key可以是什么类型? -
你能确认这就是你想要的吗? mypy-play.net/…。所有类型检查,
take_widget有效地采用Widget[Any, Any](您的示例的主要错误是您使用K作为键,但K_co作为Protocol的参数)。如果游乐场 sn-p 是您想要的行为,我会写一个答案。 -
@MarioIshac 这看起来很有希望!当然比我得到的更远。我注意到对于协议,mypy “希望” 参数类型“更矛盾”,返回类型“更协变”。我曾尝试对两者都使用
K_co,但这似乎会导致更多错误。这至少给了我一个协议和一个函数,我可以用相同的类型来加强它!
标签: python generics types mypy structural-typing