[英]Why doesn't parameter type "Dict[str, Union[str, int]]" accept value of type "Dict[str, str]" (mypy)
I have a type for a dictionary of variables passed to a template:我有一个传递给模板的变量字典的类型:
VariablesDict = Dict[str, Union[int, float, str, None]]
Basically, any dictionary where the keys are strings and the values are strings, numbers or None.基本上,键是字符串,值是字符串、数字或无的任何字典。 I use this type in several template related functions.我在几个与模板相关的功能中使用了这种类型。
Take this example function:以 function 为例:
def render_template(name: str, variables: VariablesDict):
...
Calling this function with a dictionary literal works fine:使用字典文字调用此 function 可以正常工作:
render_template("foo", {"key": "value"})
However, if I assign the dictionary to a variable first, like this:但是,如果我首先将字典分配给一个变量,如下所示:
variables = {"key": "value"}
render_template("foo", variables)
Mypy gives an error: Mypy 报错:
Argument 2 to "render_template" has incompatible type "Dict[str, str]"; “render_template”的参数 2 具有不兼容的类型“Dict[str, str]”; expected "Dict[str, Union[int, float, str, None]]"预期“字典 [str, Union[int, float, str, None]]”
It seems to me that any value of type Dict[str, str]
should be safe to pass to a function that expects a parameter of type Dict[str, Union[int, float, str, None]]
.在我看来,任何Dict[str, str]
类型的值都应该可以安全地传递给 function ,它需要一个Dict[str, Union[int, float, str, None]]
类型的参数。 Why doesn't that work by default?为什么默认情况下不起作用? Is there anything I can do to make this work?我能做些什么来完成这项工作吗?
The reason it doesn't work is that Dict
is mutable, and a function which accepts a Dict[str, int|float|str|None]
could therefore reasonably insert any of those types into its argument.它不起作用的原因是Dict
是可变的,因此接受Dict[str, int|float|str|None]
的 function 可以合理地将任何这些类型插入其参数中。 If the argument was actually a Dict[str, str]
, it now contains values that violate its type.如果参数实际上是Dict[str, str]
,它现在包含违反其类型的值。 (For more on this, google "covariance/contravariance/invariance" and "Liskov Substitution Principle" -- as a general rule, mutable containers are invariant over their generic type[s].) (关于这方面的更多信息,谷歌“协变/逆变/不变性”和“Liskov 替换原则”——作为一般规则,可变容器在其泛型类型上是不变的。)
As long as render_template
doesn't need to modify the dict you pass to it, an easy fix is to have it take a Mapping
(which is an abstract supertype of dict
that doesn't imply mutability, and is therefore covariant) instead of a Dict
:只要render_template
不需要修改您传递给它的 dict,一个简单的解决方法是让它采用Mapping
(它是dict
的抽象超类型,并不意味着可变性,因此是协变的)而不是Dict
:
def render_template(name: str, variables: Mapping[str, Union[int, float, str, None]]):
...
One minor edit gets this to work:一个小的编辑使它起作用:
variables: VariablesDict = {"key": "value"}
The MyPy docs list three solutions to the invariance problem: MyPy 文档列出了不变性问题的三种解决方案:
FWIW, the MyPy error message in this case is pretty good. FWIW,这种情况下的 MyPy 错误消息非常好。 It first gives the difference between the inferred type and the expected type, then it refers you to the relevant section of the docs, and lastly, it makes an appropriate suggestion:它首先给出推断类型和预期类型之间的区别,然后将您引向文档的相关部分,最后提出适当的建议:
tmp12.py:12: error: Argument 2 to "render_template" has incompatible type "Dict[str, str]"; expected "Dict[str, Union[int, float, st
r, None]]"
tmp12.py:12: note: "Dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
tmp12.py:12: note: Consider using "Mapping" instead, which is covariant in the value type
It seems to me that any value of type
Dict[str, str]
should be safe to pass to a function that expects a parameter of typeDict[str, Union[int, float, str, None]]
.在我看来,任何Dict[str, str]
类型的值都应该可以安全地传递给 function ,它需要一个Dict[str, Union[int, float, str, None]]
类型的参数。
Unfortunately, that assumption is wrong.不幸的是,这种假设是错误的。 Because dict
is mutable, it must be invariant in its second type parameter.因为dict
是可变的,所以它的第二个类型参数必须是不变的。
Why doesn't that work by default?为什么默认情况下不起作用?
It doesn't work because it is not type-safe: render_template
expects an argument of type dict[str, Union[int, float, str, None]]
, which means that (among other things), it expects to be able to store an int
in the dict
.它不起作用,因为它不是类型安全的: render_template
需要一个类型为dict[str, Union[int, float, str, None]]
的参数,这意味着(除其他外)它希望能够在dict
中存储一个int
。
However, you cannot store an int
in a dict[str, str]
, therefore MyPy rightfully rejects the code.但是,您不能将int
存储在dict[str, str]
中,因此 MyPy 理所当然地拒绝了该代码。
Is there anything I can do to make this work?我能做些什么来完成这项工作吗?
The difference between the two snippets of code is that type inference in MyPy is local .两个代码片段之间的区别在于 MyPy 中的类型推断是local 。 Therefore, in the second example, MyPy does not take into account how the variable variables
is used later on.因此,在第二个示例中,MyPy 没有考虑变量variables
以后如何使用。 It only infers its type from the assignment expression, and it tries to infer the most strict type possible.它仅从赋值表达式中推断其类型,并尝试推断出最严格的类型。
Now, as I mentioned above, the problem is that dict
is mutable.现在,正如我上面提到的,问题在于dict
是可变的。 Therefore, if you can make it immutable , your code will work.因此,如果你可以让它不可变,你的代码就可以工作。
Thankfully, there is just the type for you: Mapping
.值得庆幸的是,只有适合您的类型: Mapping
。 Mapping
s are immutable, therefore, they can be covariant in their second type parameter. Mapping
是不可变的,因此,它们的第二个类型参数可以是协变的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.