Generic protocols: mypy error: Argument 1 has incompatible type ...; expected
Generic protocols: mypy error: Argument 1 has incompatible type ...; expected
我正在尝试实施通用协议。我的意图是拥有一个 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
完全兼容,这让我觉得我在某些方面使用了 generics/protocol 错误,或者 mypy 无法处理这个问题。
command/error 来自 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)
将我在评论中的内容放在这里。
为了使你的问题的示例工作,你需要使输入参数 contravariant 和输出参数 covariant 像这样:
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]
for widget_3
,这意味着 Widget[str, Cat]
是 [=13] 的子类=].
Widget[str, Cat]
可以采用所有 SubStr
加上其他 str
子类型(子类关系中的输入类型可以不那么具体,因此是逆变的)并且可以有一个输出是至少一个 object
,加上 str
属性(子类关系中的输出类型可以更具体,因此协变)。另见 Wikipedia - Function Types
,它正式说明了这一观察:
For example, functions of type Animal -> Cat
, Cat -> Cat
, and Animal -> Animal
can be used wherever a Cat -> Animal
was expected.
In other words, the → type constructor is contravariant in the parameter (input) type and covariant in the return (output) type.
我正在尝试实施通用协议。我的意图是拥有一个 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
完全兼容,这让我觉得我在某些方面使用了 generics/protocol 错误,或者 mypy 无法处理这个问题。
command/error 来自 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)
将我在评论中的内容放在这里。
为了使你的问题的示例工作,你需要使输入参数 contravariant 和输出参数 covariant 像这样:
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]
for widget_3
,这意味着 Widget[str, Cat]
是 [=13] 的子类=].
Widget[str, Cat]
可以采用所有 SubStr
加上其他 str
子类型(子类关系中的输入类型可以不那么具体,因此是逆变的)并且可以有一个输出是至少一个 object
,加上 str
属性(子类关系中的输出类型可以更具体,因此协变)。另见 Wikipedia - Function Types
,它正式说明了这一观察:
For example, functions of type
Animal -> Cat
,Cat -> Cat
, andAnimal -> Animal
can be used wherever aCat -> Animal
was expected.
In other words, the → type constructor is contravariant in the parameter (input) type and covariant in the return (output) type.