通用协议:mypy错误:参数1具有不兼容的类型..;预期



我正在尝试实现一个通用协议。我的意图是有一个Widget[key_type,value_type]协议和一个简单的getter。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)

把我的评论放在这里。

为了让你的问题的例子发挥作用,你需要让输入参数逆变和输出参数协变像这样:

from typing import TypeVar
from typing_extensions import Protocol, runtime_checkable
T_co = TypeVar("T_co", covariant=True)  # General type or subclass
K_contra = TypeVar("K_contra", contravariant=True)  # ID/Key Type or supertype
class Animal: ...
class Cat(Animal): ...
@runtime_checkable
class Widget(Protocol[K_contra, T_co]):
def get(self, key: K_contra) -> T_co: ...
class ActualWidget:
def get(self, key: str) -> Cat:
return Cat()
def takes_widget(widg: Widget):
return widg
class StrSub(str):
pass
if __name__ == '__main__':
widget_0: Widget[str, Cat] = ActualWidget()
widget_1: Widget[StrSub, Cat] = ActualWidget()
widget_2: Widget[str, object] = ActualWidget()
widget_3: Widget[StrSub, object] = ActualWidget()
takes_widget(widget_0)
takes_widget(widget_1)
takes_widget(widget_2)
takes_widget(widget_3)

作为Widget[str, Cat]ActualWidget()然后可分配给widget_3Widget[SubStr, object],这意味着Widget[str, Cat]Widget[SubStr, object]的子类。

Widget[str, Cat]可以取所有SubStrs加上其他str子类型(子类关系中的输入类型可能不太具体,因此是相反的(,并且可以具有至少是object加上具有str属性的输出(子类关联中的输出类型可能更具体,因此具有协方差(。另见Wikipedia - Function Types,它将这种观察形式化:

例如,类型为Animal -> CatCat -> CatAnimal -> Animal的函数可以用于预期Cat -> Animal的任何位置。

换句话说→类型构造函数在参数(输入(类型中是逆变的,在返回(输出(类型中则是协变的。

最新更新