我有一个传递给模板的变量字典的类型:
VariablesDict = Dict[str, Union[int, float, str, None]]
基本上,任何字典,其中键是字符串,值是字符串、数字或 None。我在几个与模板相关的函数中使用这种类型。
以这个示例函数为例:
def render_template(name: str, variables: VariablesDict):
...
使用字典文字调用此函数效果很好:
render_template("foo", {"key": "value"})
但是,如果我首先将字典分配给变量,如下所示:| ||Mypy 给出错误:
variables = {"key": "value"}
render_template("foo", variables)
“render_template”的参数 2 具有不兼容的类型“Dict[str, str]”;预期“Dict [str,Union [int,float,str,None]]”
在我看来,任何
类型的值都应该安全地传递给需要
Dict[str, str]
类型参数的函数。为什么默认情况下不起作用?我能做些什么来让这项工作成功吗?
Dict[str, Union[int, float, str, None]]
. Why doesn't that work by default? Is there anything I can do to make this work?
遇到此行为的原因是,即使
Dict[str, str]
看起来像是
Dict[str, Union[int, float, str, None]]
的子类型,但类型提示在 Python 中的工作方式并非如此。让我们分解一下:
不变性与可变性
关键问题在于字典是一种可变数据结构。当将类型注释为
Dict[str, Union[int, float, str, None]]
时,是在说该字典可以存储字符串键以及可以是整数、浮点数、字符串或无的
值
。
现在,如果传递
Dict[str, str]
,它最初似乎是安全的,因为字符串值也符合联合类型。但是,问题在于
Dict[str, str]
类型的变量
以后
可能会被更新为包含的不仅仅是字符串。
请考虑以下示例:
def render_template(name: str, variables: Dict[str, Union[int, float, str, None]]):
...
variables: Dict[str, str] = {"key": "value"} # Initially fine
render_template("foo", variables)
variables["another_key"] = 10 # 潜在问题!
在这个修改后的示例中,我们将一个整数值分配给
variables
字典中的一个新键。这在技术上对于
variables: Dict[str, str]
是有效的,但会破坏
render_template
函数的预期,该函数预计该字典的值只能是联合类型中的类型。
Mypy 的严格性
Mypy 旨在通过及早发现潜在错误来帮助。在的情况下,Mypy 正确地标记了类型不匹配,因为它不能保证
Dict[str, str]
将始终仅包含字符串值。
解决方案
可以通过以下几种方式解决此问题:
-
更具体的类型注释:
如果确定传递给
render_template
的字典将始终仅包含字符串值,可以简单地更新类型注释以匹配:
```python def render_template(name: str, variables: Dict[str, str]): ...
variables = {"key": "value"} render_template("foo", variables) ```
-
类型转换:
可以将传递给
render_template
的字典强制转换为Dict[str, Union[int, float, str, None]]
:
python
variables = {"key": "value"}
render_template("foo", cast(Dict[str, Union[int, float, str, None]], variables))
-
使用
typing.TypedDict
: 对于更复杂的字典结构,其中需要对每个键具有特定的类型,可以使用typing.TypedDict
:
```python from typing import TypedDict
class TemplateVariables(TypedDict): key: str another_key: Union[int, float, None]
def render_template(name: str, variables: TemplateVariables): ...
variables: TemplateVariables = {"key": "value", "another_key": 10} render_template("foo", variables) ```
总之, 虽然看起来违反直觉,但 Mypy 的行为旨在确保类型安全并防止运行时错误。通过理解 Python 中不变性和可变性的概念以及 Mypy 的严格性,可以编写更安全、更可靠的代码。
标签:python,python-typing,mypy From: 73603289