【问题标题】:Static type analysis with Python dicts使用 Python dicts 进行静态类型分析
【发布时间】:2020-02-18 11:49:43
【问题描述】:

有人能解释一下为什么这段代码虽然有效,但会让 mypy 静态分析器以多种方式抱怨:

ranges = dict()
ranges['max'] = 0
ranges['services'] = []
ranges['services'].append('a')

即:

error: Incompatible types in assignment (expression has type "List[<nothing>]", target has type "int")

error: "int" has no attribute "append"

如果我只是在ranges: dict = dict() 的初始变量中添加一个类型提示,它就可以正常工作。

我很困惑为什么静态分析器不能自己解决这个问题,尤其是当我使用dict 关键字在第一个实例中初始化字典时。

【问题讨论】:

    标签: python mypy


    【解决方案1】:

    字典通常用作键控集合,最重要的操作是查找与 任意键关联的值。通常在字典中,每个键都具有相同的类型,每个值都具有相同的类型;如果值是异构的,那么表达式ranges[key] 不一定具有特定类型(尽管您可以将其表示为联合)。

    在您的代码中,静态分析器正在尝试推断您的字典的类型。它期望的类型是Dict[K, V] 的形式,其中KV 尚未确定。第一个赋值 ranges['max'] = 0 给出了关于两个未知数的信息:K 似乎是 strV 似乎是 int。所以此时,ranges 被推断为类型Dict[str, int]

    接下来的两行会给出错误,因为空列表不能用作Dict[str, int] 中的值,而来自Dict[str, int] 的值没有append 方法。

    显式类型注释ranges: dict = dict() 通过指定这是一个异构字典来推翻默认行为,因此值不必都具有相同的类型。鉴于这些信息,静态分析器不会假设因为其中一个值是 int,所以它们都必须是 ints。

    【讨论】:

    • 优秀,简洁的答案。非常感谢。
    【解决方案2】:

    由于您没有提供任何注释,mypy 必须尝试推断类型。它可以做出一些选择:

    • ranges = dict() 暗示range: Dict[Any, Any]

      这将接受您的所有代码。它还会接受ranges['services'].keys() 或任何其他操作,因为它会清除所有类型信息。

    • ranges['max'] = 0 暗示range: Dict[str, int]

      这是mypy 选择的。它拒绝任何不是整数的键,特别是您的list

    • ranges['services'] = [] 暗示range: Dict[str, list]

      这仅接受您的 ranges['services'] 字段。那么ranges['max'] 是无效的,因为它不是list

    • ranges['max'] = 0 ranges['services'] = []暗示range: Dict[str, Union[int, list]]

      这接受赋值。它拒绝ranges['services'].append('a'),因为ranges['services'] 可能int

    基本上,这些都不起作用。使用普通的Dict[K, V],您的代码无法正确输入。


    您可以使用typing.TypedDict 显式注释ranges。由于dict 通常不限于静态键值对,mypy 不会自行推断。

    class Ranges(TypedDict):
        max: int
        services: List[str]
    
    ranges: Ranges = dict()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-26
      • 2015-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多