如何解决通用协议:mypy 错误:参数 1 的类型不兼容……;预期的
我正在尝试实现通用协议。我的意图是拥有一个带有简单 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)
解决方法
把我的评论写在这里。
要使您的问题示例起作用,您需要使输入参数 逆变 和输出参数 协变 像这样:
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)
ActualWidget()
是一个 Widget[str,Cat]
,然后可以将 Widget[SubStr,object]
的 widget_3
赋值给 Widget[str,Cat]
,这意味着 Widget[SubStr,object]
是 Widget[str,Cat]
的子类.
SubStr
可以采用所有 str
加上其他 object
子类型(子类关系中的输入类型可以不那么具体,因此是逆变的)并且可以具有至少为str
,加上具有 Animal -> Cat
属性(子类关系中的输出类型可以更具体,因此是协方差)。另请参阅 Wikipedia - Function Types
,它使此观察形式化:
例如,Cat -> Cat
、Animal -> Animal
和 Cat -> Animal
类型的函数可以在需要 {{1}} 的任何地方使用。
换句话说,→ 类型构造函数在参数(输入)类型中是逆变的,在返回(输出)类型中是协变的。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。