dict.setdefault()的默认值被复用是因为default参数在调用时求值一次并复用同一对象;正确做法是用defaultdict或显式检查+构造,避免可变默认值污染。

为什么 dict.setdefault() 的默认值会被复用?
因为 setdefault(key, default) 中的 default 是**求值一次、传入一次**的表达式结果。如果你写 setdefault('items', []),空列表 [] 在调用时创建一次,之后所有未命中 key 都会返回并复用这个**同一个列表对象**——这不是“每次新建”,而是“统一复用”。常见症状是多个 key 对应的 list 互相污染。
用 lambda 或函数延迟构造默认值
把对象创建逻辑包进可调用对象里,让每次真正需要时才执行构造。最常用的是 lambda:
data = {}
# 每次 key 不存在时,才执行 lambda() → 新建一个空 list
data.setdefault('a', lambda: []).call() # ❌ 错!setdefault 不会自动调 lambda所以不能直接传 lambda 给 setdefault。正确做法是手动检查 + 构造:
if key not in d: d[key] = []- 或封装成工具函数:
def get_new_list(d, key): return d.setdefault(key, [])—— 这仍不解决问题
真正有效的写法是绕过 setdefault,改用 dict.get() + 赋值:
立即学习“Python免费学习笔记(深入)”;
d = {}
val = d.get('items')
if val is None:
val = []
d['items'] = val但更简洁的是用 collections.defaultdict,它天生支持“按需调用工厂函数”:
from collections import defaultdict d = defaultdict(list) # list 是类,每次调用 list() 得新实例 d['a'].append(1) d['b'].append(2) # d['a'] 和 d['b'] 是两个独立 list
如果必须用 setdefault,怎么安全传可变默认值?
唯一办法:把构造逻辑写成函数调用表达式,确保每次调用 setdefault 时都重新执行。例如:
- ✅
d.setdefault('x', [].copy())—— 但[].copy()总是返回新空列表,适合简单类型 - ✅
d.setdefault('y', some_class())—— 如果some_class是类名,每次调用都实例化新对象 - ✅
d.setdefault('z', my_factory())——my_factory必须是**函数调用**(带括号),不是函数名
错误示范:
- ❌
d.setdefault('bad', list)—— 传的是类本身,不是调用结果,setdefault会直接存list类对象 - ❌
d.setdefault('worse', lambda: [])—— 存的是 lambda 函数对象,不是列表
defaultdict 和 setdefault 的本质区别在哪?
defaultdict 的 default_factory 是在**内部缺失键访问时自动调用**的;而 setdefault 的 default 参数是**外部求值后传入的静态值**。这是根本差异,不是语法糖级别。
所以当你需要“每次都是全新对象”,且场景是高频、多 key、嵌套结构(比如 defaultdict(lambda: defaultdict(list))),defaultdict 不仅更安全,还更易读。硬用 setdefault 只会逼你写重复的 if not in … = … 检查逻辑。
容易被忽略的一点:如果默认值构造开销大(比如要读文件、连数据库),用 defaultdict 就可能误触发;这时反而得用显式的 if key not in d: d[key] = expensive_init(),把控制权完全收回来。







