首页 > 解决方案 > 是否有适用于字符串键控、嵌套 JSONish dicts 的 python 类型注释?

问题描述

我想在我的项目中对某些数据结构进行类型注释,这些数据结构是解码一些 JSON 数据的结果。

叶很简单:

ValueType = Union[int,str,float]

键只是字符串,所以这也很容易:

str

但问题是我的结构可以嵌套,所以我想做类似以下的事情(但没有明显失败的自引用):

ValueType = Union[int,str,float]
NestedDictType = Mapping[str,Union[NestedDictType,ValueType]]  # Fails!

我想我可以在一个循环中构建一些东西,但是做这样的事情(疯狂)基本上会破坏类型注释的意义,因为 NestedDictType 的结构不能静态确定。

from typing import Mapping, Type, Union

ValueType = Union[int, str, float]


def make_nested_dict_type(depth: int) -> Type:
    # Does not actually work, sorry!
    valueType = Union[make_nested_dict_type(depth - 1)] if depth else ValueType
    return Mapping[str, valueType]


NestedDictType: Type = make_nested_dict_type(4)

foo: NestedDictType = {"a": {"b": {"c": 3, "d": "four", "e": 5.6}}}

那么如何才能简洁地写出这种类型注解呢?

标签: python

解决方案


从 Python 3.7 开始,这似乎不是 100% 实用的:

类型系统可以定义递归类型结构(使用字符串前向引用),但是,MyPy 和 PyCharm 处理类型的递归部分,就好像它们是Any一样。

我能得到的最接近的是手动将递归类型扩展到多个级别。这是一个部分成功的例子。

import datetime
from typing import Mapping, Union, Any

# Recursive structure has been manually expanded to 3 levels, with the forward ref removed.
NestedDictType = Mapping[str,Union[int,str,float,Mapping[str,Union[int,str,float,Mapping[str,Union[int,str,float,Any]]]]]]


# MyPy correctly determines that this is valid
foo: NestedDictType = {"a": {"b": {"c": 3, "d": "four", "e": datetime.date.today()}}}

# MyPy correctly determines that this is bogus
bar: NestedDictType = {"a": datetime.date.today()}

# MyPy cannot detect the error because it's > 3 depth
baz: NestedDictType = {"a":{"b":{"c":{"d":datetime.date.today()}}}}

推荐阅读