python 3.9+的|运算符用于合并字典,返回新字典,右字典同名键值覆盖左字典,不修改原字典,不支持嵌套合并,仅适用于python 3.9及以上版本。

用 | 运算符合并字典(Python 3.9+)
Python 3.9 起原生支持字典并集运算符 |,右侧字典的键值会覆盖左侧同名键,天然保留“最新”值。这是最简洁、可读性最强的方式。
常见错误是误以为 |= 是就地更新——它确实是就地修改,但若用于变量复用场景,可能引发意外引用问题:
-
dict1 = {"a": 1, "b": 2};dict2 = {"b": 3, "c": 4};merged = dict1 | dict2→{"a": 1, "b": 3, "c": 4} - 若写成
dict1 |= dict2,dict1本身被修改,后续若其他变量引用了原dict1,行为可能不符合预期 - 该运算符不支持嵌套字典递归合并,仅做顶层覆盖
兼容旧版本:用 {**d1, **d2} 解包(Python 3.5+)
在 3.5 到 3.8 版本中,字典解包是最常用且高效的方法。语义和 | 完全一致:后出现的键覆盖先出现的。
注意两个易踩坑点:
- 所有解包对象必须是 mapping 类型,不能是
None或普通对象,否则报TypeError: unpacking argument must be a mapping - 如果
d2是空字典({}),解包仍有效;但如果d2是None,必须提前判断或用or {}防御:{**d1, **(d2 or {})} - 性能上略慢于
|(因涉及临时字典构造),但日常使用无感知
需要深度合并嵌套字典时别硬套 | 或解包
上面两种方法都只处理顶层键。如果字典含嵌套结构(比如 {"user": {"name": "A", "age": 25}}),且你希望 {"user": {"age": 26}} 只更新子字段而非整个 "user" 键,就必须手动递归或借助第三方库。
简单场景可用 copy.deepcopy + 递归更新,但要注意:
- 直接修改原字典易出错,建议始终返回新字典
- 遇到 list、set 等非 mapping 类型要跳过递归,否则报错
-
dict.update()对嵌套无效,它只会把整个嵌套 dict 当作一个值替换掉
性能与兼容性取舍要点
除非明确需支持 Python dict(d1, **d2) 这种老式写法——它要求所有键为字符串,且在键非 str 时直接抛 TypeError。
实际选型建议:
- Py3.9+:无条件优先用
d1 | d2,语义清晰、性能好、无隐式类型限制 - Py3.5–3.8:用
{**d1, **d2},比copy()+update()更快更安全 - 需 deep merge:用
deepmerge库或自己写轻量递归函数,避免重复造轮子时忽略不可变类型(如 tuple 作 key)的边界情况
真正容易被忽略的是键的哈希稳定性:如果字典的键本身是自定义对象,且其 __hash__ 或 __eq__ 行为在合并过程中可能变化,合并结果可能不符合直觉。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











