【问题标题】:How to use static type checking using Dict with different value types in Python 3.6?如何在 Python 3.6 中使用具有不同值类型的 Dict 进行静态类型检查?
【发布时间】:2018-01-01 16:04:51
【问题描述】:

尝试在 Python 代码中使用静态类型,所以mypy 可以帮助我解决一些隐藏的错误。使用单个变量非常简单

real_hour: int = lower_hour + hour_iterator

列表和字典更难使用,需要导入额外的typing库:

from typing import Dict, List
hour_dict: Dict[str, str] = {"test_key": "test_value"}

但主要问题 - 如何将它与具有不同值类型的字典一起使用,例如:

hour_dict = {"test_key": "test_value", "test_keywords": ["test_1","test_2"]}

如果我不对此类字典使用静态类型 - mypy 会显示错误,例如:

len(hour_dict['test_keywords'])
- Argument 1 to "len" has incompatible type

那么,我的问题是:如何将静态类型添加到此类字典中? :)

【问题讨论】:

  • 如果 dict 可以包含的所有类型都是已知的,您可以使用Union。例如Dict[str, Union[list, str]]。但是,这并不能确保特定的键始终是指定的类型,它只允许值是(例如)字符串或列表。
  • (另外,它是Dict[str, str],而不是Dict[str:str]。)

标签: python python-3.x static-typing mypy


【解决方案1】:

您需要某种Union 类型。

from typing import Dict, List, Union

# simple str values
hour_dict: Dict[str, str] = {"test_key": "test_value"}

# more complex values
hour_dict1: Dict[str, Union[str, List[str]]] = {
    "test_key": "test_value", 
    "test_keywords": ["test_1","test_2"]
}

一般来说,当您需要“这个或那个”时,您需要一个Union。在这种情况下,您的选项是strList[str]

有几种方法可以解决这个问题。例如,您可能想要定义类型名称以简化内联类型。

OneOrManyStrings = Union[str, List[str]]

hour_dict2: Dict[str, OneOrManyStrings] = {
    "test_key": "test_value", 
    "test_keywords": ["test_1","test_2"]
}

我还建议为了简单性、并行性和规律性,即使只有一项,也要使您的所有 dict 值纯 List[str]。这将允许您始终获取一个值的len(),而无需事先进行类型检查或保护条件。但这些都是小问题。

【讨论】:

  • 附加问题:如何对日期时间对象使用静态类型?
  • @sortas 一旦定义或导入了一个类型,它就可以像 intstr 这样的内置函数一样在类型化 Python 中使用。所以在from datetime import datetime 之后你可以说right_now: datetime = datetime.utcnow()。或者您可以在Unions 中使用datetime 或作为ListDict 的子类型,或者以其他方式使用,就像可以使用原始类型一样。
  • Enuice 好的,明白了,谢谢。尽管如此,mypy 不喜欢 Union:我将 dict 定义为 order_data: Dict[str, Union[str, int, List[str]]],但是当我尝试从中获取数据时 (keywords_to_load: List[str] = order_data['keywords']) mypy 说“不兼容的类型,表达式的类型为 Union[str, int, List[str]],变量的类型为 List[str] . 我是不是做错了什么?我的意思是,这不是一个真正的问题,仍然希望尽可能多地修复错误:)
  • @sortas 问题是 mypy 无法保证该操作的安全性。是的,它可能是从order_data['keywords'] 检索到的List[str] 值。但它可能同样是strint。 Mypy 不知道,因此它没有通过检查。请改用something like this。这是 mypy 可以保证的类型方程。
【解决方案2】:

虽然使用Union 确实是一种方法,但更精确的解决方案是使用(目前是实验性的)TypedDict 类型,它允许您为每个字符串键分配特定类型。

为了使用这种类型,你必须首先使用 pip 安装mypy_extensions 3rd 方库。然后,您可以执行以下操作:

from typing import List
from mypy_extensions import TypedDict

MyDictType = TypedDict('MyDictType', {
        'test_key': str, 
        'test_keywords': List[str],
})

hour_dict: MyDictType = {
    "test_key": "test_value", 
    "test_keywords": ["test_1","test_2"]
}

请注意,我们需要明确表示 hour_dict 的类型为 MyDictType。一种更简洁的方法是使用 MyDictType 作为构造函数——在运行时,MyDictType(...)dict(...) 完全相同,这意味着下面的代码与上面的行为完全相同:

hour_dict = MyDictType(
    test_key="test_value", 
    test_keywords=["test_1","test_2"]
)

最后,请注意使用 TypedDict 有一些限制:

  1. 仅当 dict 包含具有在编译时已知的类型的特定键时才有用 - 如果您期望真正动态的 dict,则应使用常规 Dict[...]
  2. 键必须都是字符串。
  3. 目前,这种类型只有 mypy 才能理解(尽管我知道一旦经过更多的实战测试,有计划最终将 TypedDict 添加到 PEP 484,这意味着任何符合 PEP 484 的类型检查器都需要支持它)。

TypedDict 旨在让在编写序列化/反序列化逻辑时更容易使用 JSON blob/dicts,这就是这些限制的原因。)

【讨论】:

    猜你喜欢
    • 2011-08-26
    • 2018-12-31
    • 1970-01-01
    • 2015-02-08
    • 2019-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多