dict.fromkeys() 的默认值会共享引用,因为它将同一对象直接赋给所有键而不拷贝;改用字典推导式 {k: [] for k in keys} 或 defaultdict(list) 可确保每个值为独立对象。

为什么 dict.fromkeys() 的默认值会共享引用
当你用 dict.fromkeys(['a', 'b', 'c'], []) 创建字典时,所有键('a'、'b'、'c')的值都指向**同一个空列表对象**。这不是 bug,而是 dict.fromkeys() 的设计逻辑:它把传入的 value 参数直接赋给每个键,不做拷贝。
后果很明显:修改其中一个键对应的列表,其他键的值也会变:
dd = dict.fromkeys(['a', 'b', 'c'], [])
dd['a'].append(1)
print(dd) # {'a': [1], 'b': [1], 'c': [1]} —— 全变了用字典推导式生成独立对象
最常用、最直观的替代方案是字典推导式,它每次迭代都会调用构造函数,确保每个值都是新对象:
-
{k: [] for k in ['a', 'b', 'c']}→ 每个值是独立的空列表 -
{k: {} for k in keys}→ 独立字典 -
{k: set() for k in keys}→ 独立集合 - 若需可变默认值(如带初始内容的列表),写成
{k: [0] * n for k in keys}(注意:仅适用于不可变元素;若元素可变,仍需用[0 for _ in range(n)]避免共享)
用 defaultdict 延迟创建独立实例
如果你实际需要的是「访问时才初始化默认值」,defaultdict 更合适,且天然避免共享问题:
立即学习“Python免费学习笔记(深入)”;
from collections import defaultdict-
dd = defaultdict(list)→ 每次对未存在的键做dd['x'].append(...),都会新建一个list() - 注意:
defaultdict不会为已存在的键自动触发工厂函数;它只在__missing__时调用,所以不会污染已有键的值 - 如果后续想转成普通
dict,直接dict(dd)即可
用 map() + lambda 或 itertools.repeat()?不推荐
有人尝试 dict(zip(keys, map(lambda _: [], keys))),逻辑上可行,但可读性差、性能无优势;更危险的是误用 itertools.repeat([]) —— 它重复的是**同一个对象引用**,和 dict.fromkeys() 本质一样:
# ❌ 错误示范:还是共享 from itertools import repeat dict(zip(['a','b'], repeat([]))) # 所有值指向同一列表
真正要靠 repeat 实现独立对象,必须配合 map 和构造函数,比如 map(list, repeat(None, len(keys))),但这又绕回了比字典推导式更啰嗦的写法。
真正容易被忽略的点是:只要涉及可变对象作默认值,就必须确认「每次出现都是新实例」——不是看语法像不像创建了新对象,而是要看运行时是否真的调用了构造函数。字典推导式和 defaultdict 是少数几个能稳定满足这点的 Python 原生方案。










