You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 07:19:59