假设我有以下内容:
from collections import OrderedDict
from dataclasses import dataclass
@dataclass
class HelloWorld:
x: OrderedDict[str, int]
a = OrderedDict([("a", 0), ("c", 2), ("b", 1)])
HelloWorld(a) <--- # type error here
产生的类型错误是:
Argument of type "OrderedDict[Literal['a', 'c', 'b'], Literal[0, 2, 1]]" cannot be assigned to parameter "x" of type "OrderedDict[str, int]" in function "__init__"
"OrderedDict[Literal['a', 'c', 'b'], Literal[0, 2, 1]]" is incompatible with "OrderedDict[str, int]"
Type parameter "_KT@OrderedDict" is invariant, but "Literal['a', 'c', 'b']" is not the same as "str"
Type parameter "_VT@OrderedDict" is invariant, but "Literal[0, 2, 1]" is not the same as "int
奇怪的是,这个非常相似的代码片段不会产生错误:
from collections import OrderedDict
from dataclasses import dataclass
@dataclass
class HelloWorld:
x: OrderedDict[str, int]
HelloWorld(OrderedDict([("a", 0), ("c", 2), ("b", 1)])) # <--- no error
观察到的行为与 Python 的类型提示如何在不同情况下推断类型有关。
问题根源
问题在于,当使用
a = OrderedDict([("a", 0), ("c", 2), ("b", 1)])
初始化
a
时,mypy 会将
a
的类型推断为
OrderedDict[Literal['a', 'c', 'b'], Literal[0, 2, 1]]
。这意味着它将键和值都视为文字类型,而不是更通用的
str
和
int
。
两种解决方案
-
使用
typing.cast
进行类型转换:
from collections import OrderedDict
from dataclasses import dataclass
from typing import cast
@dataclass
class HelloWorld:
x: OrderedDict[str, int]
a = OrderedDict([("a", 0), ("c", 2), ("b", 1)])
HelloWorld(cast(OrderedDict[str, int], a)) # <-- 显式类型转换
在这里,我们使用
typing.cast
明确告诉 mypy 将
a
视为
OrderedDict[str, int]
。
-
在定义
OrderedDict
时提供类型提示:
from collections import OrderedDict
from dataclasses import dataclass
@dataclass
class HelloWorld:
x: OrderedDict[str, int]
a: OrderedDict[str, int] = OrderedDict([("a", 0), ("c", 2), ("b", 1)])
HelloWorld(a) # <-- 没有错误
在这个解决方案中,我们在定义
a
时就提供了类型提示
OrderedDict[str, int]
,这样 mypy 就不会将其推断为更严格的文字类型。
第二个代码片段为何没有错误
在的第二个代码片段中,直接将
OrderedDict([("a", 0), ("c", 2), ("b", 1)])
传递给
HelloWorld
的构造函数。在这种情况下,mypy 可以根据
HelloWorld
的
x
属性的类型注释 (
OrderedDict[str, int]
) 推断出
OrderedDict
的预期类型,因此不会产生错误。
总结
为了避免这种类型错误,最佳做法是在定义变量时始终提供类型提示,尤其是在使用
OrderedDict
等泛型类型时。这将有助于 mypy 正确推断类型并防止出现意外错误。