python字典合并无唯一最优解:3.9+推荐|(新建)或|=(就地),老版本用{d1,d2};二者均单层覆盖,自定义逻辑需手动处理。

Python 字典合并没有“唯一最优解”,选哪种方法取决于你是否要修改原字典、Python 版本、是否需要处理重复键逻辑,以及是否涉及嵌套结构。3.9+ 用 | 最简洁,但别误以为它能递归合并;老版本用 {**d1, **d2} 兼容性好,但要注意不可哈希 key 会崩;真要自定义冲突逻辑(比如求和、拼列表),就得绕开内置操作,手动处理。
用 | 还是 |=?看要不要改原字典
| 返回新字典,|= 就地更新左操作数——行为和 update() 类似,但语法更紧凑。两者都只做一层合并,右字典的键值无条件覆盖左字典同名键。
- 想保持原字典不变 → 用
d1 | d2,尤其适合函数式风格或配置叠加场景 - 明确要复用内存、避免创建副本 → 用
d1 |= d2,比d1.update(d2)少打几个字母 - 别混用:
d1 = d1 | d2和d1 |= d2效果一样,但前者多一次赋值,后者更直接 - Python SyntaxError: invalid syntax
{**d1, **d2} 在 Python 3.5+ 的实际限制
这是目前最广泛兼容的“生成新字典”写法,语义和 | 一致,但底层是字典构造器,对输入更敏感。
- 如果
d1或d2里有 list、dict 等不可哈希类型作 key,会立刻抛TypeError: unhashable type - 顺序决定覆盖方向:
{**d2, **d1}是d1覆盖d2,容易写反 - 空字典没问题,但
{**None}或{**[1,2]}直接崩溃 - 性能上略慢于
|(CPython 3.9+ 对|做了专门优化),不过日常业务几乎感知不到
重复键不能简单覆盖?得自己写逻辑
所有内置方法(update、**、|)对重复键只支持“右覆盖”,没法自动求和、取最大、拼列表。这时候必须跳出语法糖。
- 最轻量方案:先
{**d1, **d2}得到基础合并结果,再遍历d1.keys() & d2.keys(),用自定义函数重设值 - 列表类值合并(如
{"a": [1]}+{"a": [2]}→{"a": [1, 2]}),推荐用defaultdict(list)配合extend(),比手写循环清晰 - 深度合并(嵌套 dict 再合并)标准库不支持,别硬套
|——它只会把整个子 dict 替换掉,不是合并内部字段 - 第三方库如
deepmerge可用,但引入前先问自己:是不是真需要?多数接口聚合、配置合并场景,一层就够了
update() 方法的隐蔽坑
它快、省内存,但副作用明显,调试时容易踩空。
- 别写
d1 = d1.update(d2)——update()返回None,这行会让d1变成None - 如果
d2是None或非 dict 类型(比如 list),会报AttributeError或TypeError - 嵌套字典照样只替换,不会递归:若
d1 = {"x": {"y": 1}},d2 = {"x": {"z": 2}},调update()后d1["x"]变成{"z": 2},原来的y消失 - 多字典合并时,链式调用
d1.update(d2).update(d3)会报错,因为update()不返回d1,得拆成两行
真正难的不是选哪个语法,而是想清楚:这个合并操作到底要保留什么、丢弃什么、怎么处理冲突。很多 bug 来自没意识到 | 和 update() 都不递归,或者误信 ** 能安全处理任意类型 key。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











