defaultdict 初始化必须传可调用对象,如 int、list、lambda 等,用于动态生成默认值;传非可调用对象(如 0 或字符串)会报 typeerror;其默认行为仅在访问缺失键时触发,且会静默插入新键,适用于高频写入场景而非简单兜底。

defaultdict 初始化时传什么类型决定默认值
用 defaultdict 的核心不是“设默认值”,而是“指定缺失键的工厂函数”。传 int 得到 0,传 list 得到空列表,传 dict 得到空字典——它调用的是你传入类型的无参构造函数。
常见误用是写 defaultdict(0) 或 defaultdict("abc"),这会直接报 TypeError: first argument must be callable。必须传可调用对象,比如 lambda: "unknown" 或自定义函数。
-
defaultdict(int)→ 缺失键返回0 -
defaultdict(list)→ 缺失键返回[] -
defaultdict(lambda: "N/A")→ 缺失键返回字符串"N/A" -
defaultdict(dict)→ 缺失键返回{}(注意不是嵌套的defaultdict)
defaultdict 无法用字面量语法初始化默认值
不能像普通字典那样用 {'a': 1, 'b': 2} 初始化后还保留默认行为——defaultdict 的默认逻辑只在 __missing__ 被触发时生效,而字面量赋值不会触发它。一旦你手动赋值一个键,它就只是个普通键值对;没赋值的键才走默认逻辑。
想预设部分键+保留默认行为,得先创建空 defaultdict,再用 .update() 或循环赋值:
from collections import defaultdict
d = defaultdict(str)
d.update({'name': 'Alice', 'age': '30'}) # 这样可以
# d = defaultdict(str, {'name': 'Alice'}) # ❌ 会报错:unexpected keyword argument
嵌套 defaultdict 容易漏掉内层初始化
想实现类似 d['users']['alice']['scores'].append(95) 这种结构,很多人只写一层 defaultdict(list),结果访问 d['users'] 时没问题,但 d['users']['alice'] 仍会抛 KeyError ——因为第二层还是普通 dict。
正确做法是让内层也是 defaultdict,常用技巧是用 lambda 套娃:
from collections import defaultdict nested = defaultdict(lambda: defaultdict(list)) nested['users']['alice'].append(95) # ✅ 成功 nested['users']['bob'].append(87) # ✅ 成功
更深层嵌套建议封装成函数,避免 lambda 嵌套过深难以调试;另外注意这种结构序列化(如 json.dumps)会失败,需先转成普通 dict。
defaultdict 和普通 dict 的行为差异会影响调试
defaultdict 的 .keys()、.items() 看起来和普通 dict 一样,但它会**静默创建键**——只要访问了缺失键,该键就会被插入字典中。这意味着:
- 遍历时如果用了
for k in d: print(d[k]),可能意外新增大量键 -
if key in d:是安全的,但d[key]即使只读也会触发默认值生成并写入 - 单元测试里断言
len(d)可能因隐式插入而失败
如果只需要“读时兜底”而不改变原结构,用 dict.get(key, default) 更可控;defaultdict 真正适合的场景是高频写入+累积统计(如计数、分组),而不是替代 .get()。
最常被忽略的一点:defaultdict 的默认工厂函数每次都会重新调用,所以不要在里面放有副作用的操作(比如 defaultdict(lambda: print("created!")))——它会在每个缺失键首次访问时都打印一次。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











