json.loads()和pprint.pprint()触发RecursionError是因为深度嵌套或循环引用超出Python默认1000层递归限制,本质是调用栈过深而非内存或格式问题;应改用迭代+深度控制+循环检测来安全处理。

为什么json.loads()和pprint.pprint()会触发RecursionError?
Python默认递归限制是1000层,而深度嵌套的字典/列表(比如解析某些API返回的树形结构、自引用JSON、或意外生成的循环引用)会迅速突破这个阈值。典型报错是RecursionError: maximum recursion depth exceeded,不是数据太大,而是调用栈太深。
关键点在于:这不是内存不足问题,不能靠增加RAM解决;也不是JSON格式错误,json.loads()可能已成功解析,但后续打印或遍历时崩溃。
-
pprint.pprint()默认递归展开所有层级,遇到500+层嵌套就大概率失败 -
json.dumps()在序列化含循环引用的对象时也会递归卡死(即使用了default参数) - 手动写的递归遍历函数若没设深度计数器,同样踩坑
用sys.setrecursionlimit()是否安全?
能临时绕过错误,但不推荐作为常规解法。提高限制值(如设为3000)可能让程序在栈溢出时直接崩溃,而非抛出可捕获的RecursionError,尤其在C扩展或嵌入式环境中风险更高。
更稳妥的做法是改写逻辑,避开深层递归:
立即学习“Python免费学习笔记(深入)”;
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 用显式栈(
list)替代函数调用栈,例如DFS遍历时用stack = [(root, 0)]记录节点和当前深度 - 对
pprint加限制:pprint.pprint(data, depth=5, width=80),depth参数能强制截断嵌套 - 检查数据是否含循环引用——用
id()缓存已访问对象,发现重复id立即终止遍历
如何安全地遍历并处理嵌套结构?
核心原则:把“递归”转为“迭代”,并始终监控深度与对象唯一性。
示例:一个带深度保护和循环检测的扁平化函数:
def safe_flatten(obj, max_depth=100, seen=None):
if seen is None:
seen = set()
if id(obj) in seen:
return "[circular ref]"
if max_depth <= 0:
return "[max depth reached]"
seen.add(id(obj))
if isinstance(obj, (dict, list)):
result = {}
if isinstance(obj, dict):
for k, v in obj.items():
result[k] = safe_flatten(v, max_depth - 1, seen)
else:
result = [safe_flatten(v, max_depth - 1, seen) for v in obj]
seen.discard(id(obj))
return result
return obj注意seen.discard(id(obj))必须在递归返回前清理,否则影响同一对象在其他分支的判断。
处理JSON循环引用的特殊场景
标准json模块不支持循环引用,但有些第三方库(如jsonpickle)会注入$ref字段。若你必须处理这类数据:
- 先用
json.loads()加载原始字符串,再用safe_flatten()类函数处理结果 - 避免用
json.dumps(obj, default=str)——str()对嵌套对象仍会触发递归 - 真正需要序列化的场景,改用
orjson或ujson,它们对深度控制更严格,且报错更早、更明确
最常被忽略的是:很多所谓“大嵌套”其实是意外的无限递归(比如父节点存了子节点引用,子节点又反向引用父节点),这种必须靠id()检测,光调高递归限制只会让崩溃更晚、更难定位。

















