登录
首页 >  文章 >  python教程

Python递归遍历多层字典与JSON方法

时间:2026-03-22 22:52:04 135浏览 收藏

Python中遍历嵌套字典和JSON数据时,真正引发RecursionError的元凶往往不是层级过深,而是隐藏的循环引用;本文直击痛点,提供三大高效应对策略:用id()去重+集合缓存规避重复访问、以显式栈替代递归实现可控遍历、借助生成器yield按需提取目标字段,并深入揭示object_hook性能陷阱及非内置dict类型兼容性隐患,助你安全、灵活、高性能地处理任意复杂嵌套结构。

Python怎么遍历复杂字典_递归遍历无限层级JSON与Dict字典

遇到嵌套字典就报 RecursionError?先检查循环引用

Python 默认递归深度是 1000 层,但真正踩坑的往往不是层级深,而是字典里存在自引用(比如 a['parent'] = a)。这种结构会让递归函数无限绕圈,直到触发 RecursionError: maximum recursion depth exceeded

实操建议:

  • 遍历前用 id()is 判断是否已处理过当前对象,避免重复进入同一字典实例
  • 用集合(set())缓存已访问的 id(obj),而不是对象本身(避免不可哈希报错)
  • 如果明确不需要处理循环引用,加一层 try/except RecursionError 并提前返回提示,比让程序崩掉更可控

想安全遍历任意嵌套 dict/list 混合结构?用栈模拟递归

递归写法简洁,但不控深度、难中断、不方便加日志或过滤。用显式栈(list)替代系统调用栈,既能避免爆栈,又能随时跳过特定键或值类型。

实操建议:

  • 把初始数据和路径(如 ['data', 'items', 0, 'name'])打包成元组压栈
  • 每次弹出一个项,检查类型:是 dict 就遍历 .items(),是 listtuple 就用 enumerate(),其他类型直接处理
  • 对 key 名敏感时(比如跳过 '__meta'),在入栈前做判断,省得白跑一趟

示例关键片段:

stack = [(data, [])]  # (当前对象, 当前路径)<br>while stack:<br>    obj, path = stack.pop()<br>    if isinstance(obj, dict):<br>        for k, v in obj.items():<br>            if k == '__meta': continue  # 过滤<br>            stack.append((v, path + [k]))

需要提取所有 value 中的某个字段(比如所有 'id')?别硬写递归,用生成器 yield

多数真实需求不是“打印所有键值”,而是“收集所有满足条件的值”。这时候递归函数返回列表容易吃内存,尤其 JSON 很大时;而生成器按需产出,配合 next()itertools.islice 更灵活。

实操建议:

  • 函数用 yield 而非 return list,每找到一个匹配就产出,不攒全量
  • isinstance(obj, (dict, list)) 统一判断可迭代容器,别漏掉 tuplecollections.abc.Mapping
  • 注意字符串也是 Sequence,但通常不该进递归——加 elif isinstance(obj, str) 提前 return 避免误拆

示例:

def find_values(obj, target_key):<br>    if isinstance(obj, dict):<br>        for k, v in obj.items():<br>            if k == target_key:<br>                yield v<br>            yield from find_values(v, target_key)<br>    elif isinstance(obj, list):<br>        for item in obj:<br>            yield from find_values(item, target_key)

JSON 字符串转 dict 后遍历慢?检查是否用了 json.loads(..., object_hook=...)

有些项目为了统一处理日期或 Decimal,会传 object_hook 参数给 json.loads(),但这个钩子会对**每个 dict 实例**都调用一次。如果钩子里做了复杂逻辑(比如正则匹配 key 名),性能会断崖下跌,尤其是千级嵌套时。

实操建议:

  • 优先用 object_pairs_hook 替代 object_hook,它只在解析 dict 时触发,且能拿到原始 key-value 对,避免二次遍历
  • 如果只是想把所有数字转 Decimal,考虑用 parse_float=Decimal 这类专用参数,比通用 hook 快一个数量级
  • 不确定瓶颈在哪?用 python -m cProfile your_script.pyobject_hook 占了多少 time

最常被忽略的是:你以为在遍历 dict,其实拿到的是 collections.OrderedDict 或第三方库封装的类 dict 对象,它们的 .items() 行为可能和内置 dict 不一致,甚至不支持 isinstance(x, dict)。动手前先 type(obj).__name__ 看一眼。

今天带大家了解了的相关知识,希望对你有所帮助;关于文章的技术知识我们会一点点深入介绍,欢迎大家关注golang学习网公众号,一起学习编程~

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>