Python 3.9 的字典合并操作符是 |,它返回新字典而不修改原字典,要求左右操作数均为 dict 类型,仅支持浅层合并,键冲突时右侧值覆盖左侧。

Python 3.9 的 | 操作符能直接合并字典,但仅限于两个 dict 且不修改原对象
它不是就地更新,而是返回一个新字典。如果你写 d1 | d2,d1 和 d2 都不会变——这点和 dict.update() 完全不同,也容易误用。
常见错误是以为它能链式合并多个字典:d1 | d2 | d3 看似合理,其实合法(因为 | 左结合),但要注意中间结果也是 dict,所以语法上没问题;真正的问题在于右操作数必须是 dict,比如 d1 | {"a": 1} | some_list 会直接报 TypeError: unsupported operand type(s) for |: 'dict' and 'list'。
-
|要求左右操作数都是dict(或实现了__or__且返回 dict 的自定义类) - 键冲突时,右边字典的值覆盖左边的(和
{**d1, **d2}行为一致) - 性能略优于
{**d1, **d2},因为不涉及解包和临时关键字参数构造
为什么 dict |= dict 有时会报错?
|= 是就地合并(in-place),调用的是 dict.__ior__。它要求左操作数是 dict,右操作数也必须是 dict,否则抛 TypeError。不像 += 对 list 那样宽松。
典型翻车现场:d |= {"x": 1} 没问题,但 d |= [("x", 1)] 或 d |= {"x": 1}.keys() 都会失败,因为右值不是 dict 类型。
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
立即学习“Python免费学习笔记(深入)”;
- 右操作数必须是
dict实例,不能是dict_keys、dict_items、list、tuple等可迭代对象 - 如果想用非 dict 数据源,得先转成 dict:
d |= dict(other_iterable) -
|=修改原字典,注意别在循环中误改共享引用
和 collections.ChainMap、** 解包比有什么区别?
| 是真合并:生成一个全新 dict,所有键值对都拷贝一份;而 ChainMap 只是逻辑视图,不复制数据,也不解决键冲突(只取第一个映射中的值);{**d1, **d2} 虽然也生成新 dict,但底层要构建关键字参数,开销稍大,且不支持非字符串键(因为 ** 要求 key 是 identifier)。
-
|支持任意 hashable 键(包括数字、元组、自定义对象),{**d1, **d2}在 Python 3.9+ 其实也支持了,但历史代码里常有陷阱 -
ChainMap不适合需要“扁平化”结果的场景,比如传给需要纯 dict 的 API - 如果只是临时查值、不想拷贝内存,
ChainMap更轻量;如果后续要频繁修改或序列化,|更稳妥
嵌套字典用 | 无法递归合并
| 是浅合并(shallow merge)。遇到嵌套结构,比如 d1 = {"a": {"x": 1}} 和 d2 = {"a": {"y": 2}},d1 | d2 结果是 {"a": {"y": 2}},整个 "a" 键被替换,而不是把 {"x": 1} 和 {"y": 2} 合并。
- 没有内置的递归合并操作符,别指望
|能自动处理嵌套 - 需要递归合并时,要么手写递归函数,要么用第三方库如
deepmerge或dict_deep_update - 误以为
|是 deep merge 是新手最常踩的坑之一
实际用的时候,先确认你真的只需要一层合并,再检查右操作数是不是货真价实的 dict,尤其当它来自函数返回值或用户输入时——类型不对,错得非常安静。

















