Python中为未知结构JSON对象添加类型提示的问题
为未知结构JSON添加类型提示的问题与解决
问题背景
你尝试为外部API返回的未知/可变结构JSON定义类型提示,避免使用Any或cast(),于是定义了如下类型别名:
Json: TypeAlias = dict[str, "Json"] | list["Json"] | str | int | float | bool | None
但在访问data["results"]时,mypy抛出报错:
No overload variant of "getitem" of "list" matches argument type "str" [call-overload]
Possible overload variants:
def getitem(self, SupportsIndex, /) -> Json
def getitem(self, slice, /) -> list[Json]Value of type "dict[str, Json] | list[Json] | str | int | float | bool | None" is not indexable [index]
问题原因
你定义的Json是多类型联合,包含了字典、列表、基础数据类型和None。mypy的类型检查是严格的:它需要确保联合类型中的所有成员都支持当前操作。而这里:
- 列表仅支持整数索引/切片,不支持字符串key;
- 字符串、数字、布尔值、None根本不可被索引;
因此mypy无法确认data是支持字符串索引的字典,所以报错。
解决方案
1. 类型断言(最简单的方式)
用isinstance断言data是字典,让mypy缩小类型范围:
import requests from typing_extensions import TypeAlias Json: TypeAlias = dict[str, "Json"] | list["Json"] | str | int | float | bool | None res = requests.get("https://randomuser.me/api/?results=5&nat=gb") data: Json = res.json() # 断言data是字典,mypy会将data的类型缩小为dict[str, Json] assert isinstance(data, dict), "API返回数据不是预期的字典结构" results: Json = data["results"]
2. 自定义类型守卫(更严谨的类型缩小)
如果需要更灵活的类型检查逻辑,可以用TypeGuard定义类型守卫函数:
import requests from typing_extensions import TypeAlias from typing import TypeGuard Json: TypeAlias = dict[str, "Json"] | list["Json"] | str | int | float | bool | None def is_json_dict(obj: Json) -> TypeGuard[dict[str, Json]]: return isinstance(obj, dict) res = requests.get("https://randomuser.me/api/?results=5&nat=gb") data: Json = res.json() if is_json_dict(data): results: Json = data["results"] else: raise ValueError("API返回数据结构不符合预期")
3. 针对API约定定义具体类型(最优解,若API有基本规范)
如果API返回的结构有固定的顶层约定(比如总是包含results字段的字典),直接定义更具体的TypedDict:
import requests from typing import TypedDict, List class RandomUser(TypedDict): name: dict[str, str] email: str phone: str # 根据实际返回字段补充 class RandomUserResponse(TypedDict): results: List[RandomUser] info: dict[str, str | int] res = requests.get("https://randomuser.me/api/?results=5&nat=gb") # 直接用具体类型,无需联合类型 data: RandomUserResponse = res.json() results = data["results"]
这种方式完全避免了联合类型的模糊性,类型提示更精准,也符合类型系统的设计初衷。
内容的提问来源于stack exchange,提问作者FlightPlan
相关产品推荐
相关产品推荐

