python - 为什么 mypy 拒绝我的 "mixed union"类型声明?

标签 python annotations typing mypy

troubleshooting a semi-related problem in the Python chat 时,我在 mypy 中发现了一些我不明白的行为。

from typing import Union, List, Dict

def f(x: Union[
            Dict[str, float],
            Dict[str, str],
            Dict[str, int],
    ]):
        pass

f({"a": 1})     #passes
f({"a": "b"})   #passes
f({"a": 1.0})   #passes

def g(x: Union[
            Dict[str, float],
            Dict[str, Union[str, int]],
    ]):
        pass

g({"a": 1})     #fails
g({"a": "b"})   #fails
g({"a": 1.0})   #passes

def h(x: Dict[str, Union[float, str, int]]):
    pass

h({"a": 1})     #passes
h({"a": "b"})   #passes
h({"a": 1.0})   #passes
当我在这个脚本上执行 mypy 时,它只提示中间函数 g :
C:\Users\Kevin\Desktop>mypy test.py
test.py:20: error: Argument 1 to "g" has incompatible type "Dict[str, int]"; expected "Union[Dict[str, float], Dict[str, Union[str, int]]]"
test.py:20: note: "Dict" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance
test.py:20: note: Consider using "Mapping" instead, which is covariant in the value type
test.py:21: error: Argument 1 to "g" has incompatible type "Dict[str, str]"; expected "Union[Dict[str, float], Dict[str, Union[str, int]]]"
test.py:21: note: "Dict" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance
test.py:21: note: Consider using "Mapping" instead, which is covariant in the value type
Found 2 errors in 1 file (checked 1 source file)
(正如注释所暗示的那样,用 Dict 替换 Mapping 可以消除错误,但为了这个问题,我必须使用 Dict。)
这些错误令我感到惊讶。据我所知,每个函数的类型注释应该简化为同一组类型:一个字典,其键是字符串,值是浮点数/字符串/整数。那么为什么只有 g 有不兼容的类型呢? mypy 是否对两个联盟的存在感到困惑?

最佳答案

这是因为 Dict 是不变的。它应该是不变的,因为它是可变的。Dict[str, int] 不是 Dict[str, Union[str, int]] 的子类型(即使 intUnion[int, str] 的子类型)
如果你要做这样的事情怎么办:

d: Dict[str, Union[str, int]]
u: Dict[str, int]
d = u  # Mypy error: Incompatible type
d["Key"] = "value"

Mypy 假设字典是同质的:它们永远只包含一种类型。与此相反,例如,Tuples 旨在包含异构数据:允许每个项目具有不同的类型。
如果您需要异构 Dict ,您可以使用 TypedDict ,但只需要一组固定的字符串键:
from typing import List, TypedDict

Mytype = TypedDict('Mytype', {'x': str, 'a': List[str]})
s: Mytype = {"x": "y", "a": ["b"]}

s['a'].append('c')
笔记:

Unless you are on Python 3.8 or newer (where TypedDict is available in standard library typing module) you need to install typing_extensions using pip to use TypedDict

关于python - 为什么 mypy 拒绝我的 "mixed union"类型声明?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62900750/

相关文章:

python - 如何扩展 str.isspace()?

python - 使用 for 循环从数据框中选择一个值

iOS 显示标注而不是注释

Java POJO 属性的 xml 注释

python - 获取列表类型的元素类型

python - request.user 返回抽象用户(django-stubs)

python - 在 Python 中从字符串中获取主题标签的优雅方法?

Python 的 db-query 很慢,但 Perl 没有

Java 注释 - 如何检查带注释的元素是否实现了接口(interface)?

typing - 什么是 "top-level binding"?