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

理解嵌套defaultdict:`tree = lambda: defaultdict(tree)`与`tree = defaultdict(lambda: tree)`的差异及原理

搞懂嵌套defaultdict的两种写法:有效 vs 无效

让我一步步帮你拆解这两种写法的区别,搞清楚为什么一个能正常工作,另一个会抛出循环引用的错误~

一、有效写法的工作原理

先看这个能正常生成多层嵌套字典的写法:

tree = lambda: defaultdict(tree)
x = tree()
x["1"]
x["2"]
x["1"]["3"]
print(json.dumps(x))

这里的核心是tree是一个匿名函数,它的唯一作用就是返回一个新的defaultdict,而这个defaultdict的默认工厂正好是tree自己。

拆解执行的细节:

  1. 当你调用tree()时,会创建一个全新的defaultdict实例,这个实例的规则是:如果访问了不存在的键,就自动调用tree()生成另一个全新的defaultdict作为该键的值。
  2. 比如x["1"]:因为x是tree()生成的defaultdict,访问"1"这个不存在的键时,会触发tree(),生成一个新的defaultdict赋值给x["1"];后续x["1"]["3"]也是同理,再生成一层全新的defaultdict。
  3. 整个结构里的每一层都是独立的新实例,没有任何地方指向已经存在的实例,所以json.dumps可以顺利序列化,不会出现循环引用问题。

二、无效写法报错的根本原因

再看这个会抛出循环引用错误的写法:

tree = defaultdict(lambda: tree)
x = tree
x["1"]
x["2"]
x["1"]["3"]
print(json.dumps(x))

问题就出在循环引用的生成:

  1. 这里的tree直接就是一个defaultdict实例,它的默认工厂是lambda: tree——意思是,当访问不存在的键时,不会生成新的实例,而是直接返回tree这个已经存在的实例本身。
  2. 举个例子:当你访问x["1"]时,因为x就是tree实例,所以会调用lambda: tree,把tree自己赋值给x["1"];之后x["1"]["3"]其实就是tree["3"],同样会把tree自己赋值给tree["3"]。
  3. 这下就形成了死循环:tree["1"]指向tree,tree["3"]也指向tree,整个结构是自己引用自己的循环体。json.dumps在序列化时会检测到这种循环结构,所以直接抛出ValueError: Circular reference detected。

三、修改后的写法为什么和第一种等价

你提到的修改后的写法:

tree = lambda: defaultdict(lambda: tree())

其实这个写法和最初的有效写法是完全等价的。因为tree本身就是一个返回defaultdict(tree)的函数,tree()调用后得到的就是一个新的defaultdict实例,所以lambda: tree()和原来的tree功能完全一致——都是返回一个全新的、默认工厂为tree的defaultdict。本质上就是换了一种写法表达同一个逻辑,所以自然也能正常生成多层嵌套的字典。

内容的提问来源于stack exchange,提问作者joseville

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 18:09:03