Python 3.9+ 推荐用 | 运算符浅合并字典,语法简洁、性能最优、支持链式;|= 用于原地合并以节省内存;二者均不支持深度合并,嵌套结构会被整体覆盖。

| 运算符是 Python 3.9+ 合并多个 dict 最高效、最直观的方式,前提是只要求一层浅合并,且你不需要深度递归合并嵌套结构。
用 | 链式合并多个字典(推荐)
直接写 d1 | d2 | d3 | d4,语义清晰、语法简洁、性能最优。CPython 底层对多操作数做了优化,不是简单两两调用,而是单次遍历所有右操作数字典,避免中间对象膨胀。
- 顺序决定覆盖优先级:
d1 | d2 | d3中,d3的键会覆盖d2和d1的同名键,d2覆盖d1 - 所有操作数必须是
dict实例,传入None或list会立刻报TypeError: unsupported operand type(s) - 不支持混合类型 key(比如
{[1]: "bad"} | {"a": 1}会在解包时就崩,因为 key 不可哈希) - 如果字典来自生成器或动态构造,建议先转成
dict再合并,否则可能触发多次迭代
用 |= 原地累积合并(适合大字典或内存敏感场景)
当左侧字典较大,且你明确想复用其内存时,|= 比反复 | 更省空间。它等价于 left.update(right),但语法更紧凑、支持链式调用。
-
config |= defaults |= overrides是合法的,执行顺序从左到右,最终config被就地修改三次 - 注意副作用:一旦用了
|=,原始config就变了,别在循环里误复用 - 和
update()一样,它不检查 value 类型,如果某个right是自定义映射类但没实现items(),会抛AttributeError
为什么不用 {**d1, **d2, **d3}?
虽然兼容 Python 3.5+,但在 3.9+ 场景下它已不是首选:
- 语法冗长,尤其字典变量名长时:
{**settings, **env_config, **cli_args}不如settings | env_config | cli_args - 性能略差:每次
**都要构造临时关键字参数,在 CPython 中有额外解析开销;实测慢约 10–15% - 无法用于表达式上下文:你不能写
func(d1 | d2),但可以写func({**d1, **d2})—— 反过来,|支持函数式链式,**不支持 - 遇到不可哈希 key 时错误位置更难定位(
**在字典构造阶段报错,而|错误信息更明确指向操作符)
嵌套字典合并不是 | 的责任
| 和 |= 都只做一层合并。如果你有 {"user": {"name": "A"}} 和 {"user": {"age": 25}},结果是 {"user": {"age": 25}} —— 整个 "user" 字典被替换,不是内部字段合并。
立即学习“Python免费学习笔记(深入)”;
- 真需要深度合并,别自己硬写递归函数去处理循环引用和类型冲突,先评估业务是否真的需要:多数配置合并、API 参数融合其实只需一层
- 若确认需要,用
deepmerge库比手写更可靠;但要注意它默认策略未必符合你的覆盖逻辑(比如 list 是拼接还是替换) - 最易忽略的一点:深度合并后,原字典里的 mutable 对象(如嵌套 list)可能被共享引用,导致意外修改 —— 浅合并反而更安全


















