Mypy为什么将JSON的子集报告为无效?

huangapple go评论85阅读模式
英文:

Why does Mypy report a subset of JSON as invalid?

问题

error: "return_json"的返回类型"List[Dict[str, JSON]]"与超类型"JsonReturner"中的返回类型"JSON"不兼容 [覆盖]
error: "return_json"的返回类型"Dict[str, List[JSON]]"与超类型"JsonReturner"中的返回类型"JSON"不兼容 [覆盖]

英文:

I have a base class that returns a JSON type. I then have subclasses that return more specific types that should I think be valid JSON types, but Mypy reports an error:

error: Return type "List[Dict[str, JSON]]" of "return_json" incompatible with return type "JSON" in supertype "JsonReturner"  [override]

Have I misunderstood something or am I exploring the limits of the type-checking implementation?

Full example:

from typing import TypeAlias


JSON: TypeAlias = dict[str, "JSON"] | list["JSON"] | str | int | float | bool | None


class JsonReturner:

    def return_json(self) -> JSON:
        raise NotImplementedError("abstract base class")


class ListJsonReturner(JsonReturner):

    def return_json(self) -> list[JSON]:
        return []


class DictJsonReturner(JsonReturner):

    def return_json(self) -> dict[str, JSON]:
        return {}


class ListDictJsonReturner(JsonReturner):

    def return_json(self) -> list[dict[str, JSON]]:
        return []


class DictListJsonReturner(JsonReturner):

    def return_json(self) -> dict[str, list[JSON]]:
        return {}
$ mypy jsontypes.py
jsontypes.py:27: error: Return type "List[Dict[str, JSON]]" of "return_json" incompatible with return type "JSON" in supertype "JsonReturner"  [override]
jsontypes.py:33: error: Return type "Dict[str, List[JSON]]" of "return_json" incompatible with return type "JSON" in supertype "JsonReturner"  [override]

答案1

得分: 1

这一切都与方差有关。阅读 PEP-483 获取更多信息。

list 这样的通用类型是不变的:类型 list[T] 不是类型 list[U] 的子类型或超类型,除非 TU 是相同的类型。

同样,dict 也是不变的,因为两个具体的字典类型只有在它们的键和值类型相同时才相同。

函数类型在其返回类型方面是协变的。对于固定的参数类型,Callable[..., R1]Callable[..., R2] 的子类型,当 R1R2 的子类型时。

联合类型的每个组件都是联合的子类型。

当你覆盖一个函数时,覆盖的类型必须是父函数的子类型。只要不更改参数类型,这意味着新的返回类型必须是原始返回类型的子类型。

在你的前两个覆盖中,返回类型是 JSON 的子类型。

在最后两个覆盖中,它们不是。list[dict[str, JSON]] 不是 JSON 的子类型,因为列表是不变的。dict[str, list[JSON]] 也不是 JSON 的子类型,因为字典也是不变的。

英文:

This all has to do with variance. Read PEP-483 for more information.

Generic types like list are invariant: a type list[T] is not a subtype nor a supertype of list[U] unless T and U are the same type.

Similarly, dict is invariant because a two concrete dict types are the same only if their key and value types are the same.

Function types are covariant in their return type. For a fixed argument type, Callable[..., R1] is a subtype of Callable[..., R2]whenR1is a subtype ofR2`.

Each component of a union type is a subtype of the union.

When you override a function, the type of the override must be a subtype of the parent function. As long as you don't change the parameter types, this means the new return type must be a subtype of the original's return type.

In your first two overrides, the return types are subtypes of JSON.

In the last two, they are not. list[dict[str, JSON]] is not a subtype of JSON because lists are invariant. dict[str, list[JSON]] is not a subtype of JSON because dicts are invariant.

huangapple
  • 本文由 发表于 2023年3月7日 23:37:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/75664060.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定