python - 通用协议(protocol) : mypy error: Argument 1 h

我正在尝试实现通用协议(protocol)。我的意图是拥有一个带有简单 getter 的 Widget[key_type, value_type] 协议(protocol)。 Mypy 提示 Protocol[K, T] 所以变成了 Protocol[K_co, T_co]。我已经排除了所有其他约束,但我什至无法使最基本的情况 widg0: Widget[Any, Any] = ActualWidget() 正常工作。 ActualWidget.get 应该与 get(self, key: K) -> Any 完全兼容,这让我觉得我在某种程度上错误地使用了泛型/协议(protocol),或者 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 子类型(sublcass 关系中的输入类型可以少具体的,因此是逆变的)并且可以有一个至少是 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.

关于python - 通用协议(protocol) : mypy error: Argument 1 has incompatible type . ..;预期的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68319023/

相关文章:

reactjs - 如何在 primereact 中拥有主题切换器

python - 我的损失函数在训练期间没有得到更小的值

python - 多次拟合时 keras fit() 的历史

sql - 如何获得日期 + 时间的确定性计算值

c - 为什么 GTK 4 报告 "assertion ' GTK_IS_WIDGET(部件 )'

python - numpy 中如何实现多维数组切片/索引?

rust - 如何正确处理 Warp 路由的错误

sass - Stylelint 禁用规则, "no-descending-specificity"

django - 如何在基于 Django 类的 View 中更改模板

kubernetes - 部署后如何在 kubernetes 中删除 Traefik 2.0 中间件