简体   繁体   English

为什么参数类型“Dict [str,Union [str,int]]”不接受“Dict [str,str]”类型的值(mypy)

[英]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 文档列出了不变性问题的三种解决方案:

  1. Use an explicit type annotation (that is my answer shown above).使用显式类型注释(这是我上面显示的答案)。
  2. Make a copy of the right hand side (this is egregious).复制右侧的副本(这太令人震惊了)。
  3. Use immutable collections as annotations whenever possible: (that matches the excellent answer from @Samwise).尽可能使用不可变的 collections 作为注释:(这与@Samwise 的出色答案相匹配)。

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 type Dict[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.

相关问题 Mypy “Union[str, Dict[str, str]]”的无效索引类型“str”; 预期类型“联合[int,切片]” - Mypy Invalid index type "str" for "Union[str, Dict[str, str]]"; expected type "Union[int, slice]" “Union[List[Any], Dict[Any, Any]]”的索引类型“Union[int, str]”无效; 预期类型“int” - Invalid index type “Union[int, str]” for “Union[List[Any], Dict[Any, Any]]”; expected type “int” 为什么出现此警告“预期类型&#39;int&#39;(匹配的通用类型&#39;_T&#39;),却改为&#39;Dict [str,None]”? - Why is this warning “Expected type 'int' (matched generic type '_T'), got 'Dict[str, None]' instead”? 为什么我在输入 str 或 int 后仍无法连接我的字典? - Why can't I get concatenate my dict even after i type str or int? python如何转换<type 'str'>至<type 'dict'> - python how to convert <type 'str'> to <type 'dict'> 参数验证失败:\\n参数 Filters[0].Values[0] 的类型无效,值:{},类型:<class 'dict'> , 有效类型:<class 'str'> - Parameter validation failed:\nInvalid type for parameter Filters[0].Values[0], value: {}, type: <class 'dict'>, valid types: <class 'str'> "类型错误:+= 不支持的操作数类型:“dict”和“str”" - TypeError: unsupported operand type(s) for +=: 'dict' and 'str' 烧瓶+不支持的操作数类型:“ dict”和“ str” - Flask unsupported operand type(s) for +: 'dict' and 'str' Int + Str 类型 python - Int + Str type python pytext 中的 Dict[str, Any] 或 Dict[str, Field] - Dict[str, Any] or Dict[str, Field] in pytext
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM