发布于2026-07-10 阅读(0)
扫一扫,手机访问
Python 3.9 引入的 | 运算符,不是心血来潮的语法糖,而是专门为“字典不可变合并”这个高频场景量身定做的。它的语义非常明确:右边的键覆盖左边的键,生成一个全新字典,原对象纹丝不动。很多人一开始会误以为这是集合的并集操作,其实两者八竿子打不着——它不是数学并集,也不是深度合并,就是纯粹的、浅层的“右盖左”。

| 比 {**d1, **d2} 更值得用两者都返回新字典、不修改原对象,但关键差异在可读性、链式能力和错误提示上。先说说最常见的痛点:{**d1, **d2} 遇到键名是非字符串的情况会直接炸裂,抛出 TypeError: 'int' object is not iterable 这种让人摸不着头脑的提示。而 | 对非字符串键完全兼容,它本身就是为映射类型设计的。
链式写法更是高下立判。base | overrides | env_vars 这种写法,从左到右读下来一目了然,每个变量负责什么清清楚楚。换成 {**base, **overrides, **env_vars} 也不是不行,只是嵌套多了之后,想定位到底哪一层出了问题,眼睛容易花。
另一个容易被忽略的细节是错误提示。当右操作数不是 dict 而是 OrderedDict 或其他自定义映射时,| 会明确告诉你 “right operand must be a mapping”,定位精准。而 ** 解包往往抛出一个模糊的 TypeError: 'X' object is not iterable,排查起来要多绕几圈。
| 和 update() 到底该选哪个核心区别其实就一条:你需要保留原字典不变吗?
如果答案是“需要”——比如函数参数默认值拼接、配置分层叠加(defaults | user_config | cli_args)、构建一次性的请求 payload——那就用 |。它生成新对象,不影响原始数据。
如果答案是“不需要”——比如循环内反复累积数据、缓存字典复用、内存敏感场景——那就老老实实用 update() 或 |=。频繁创建新 dict 对象是有代价的,热路径上尤其明显。
注意一个小坑:d1 |= d2 等价于 d1.update(d2),但 |= 对右操作数的要求更严格——它只接受 dict 或实现了 items() 的类 dict 对象。而 update() 的兼容性更广,能接受任意可迭代的键值对。如果你需要尽可能宽泛的输入类型,update() 更稳妥。
看似简单,实际高频翻车点集中在类型、版本和嵌套逻辑上。
None | {} 直接报 TypeError: unsupported operand type(s) —— 必须先判空:(params or {}) | defaultslist、set、str 不能直接参与 | 运算,哪怕内容看起来像字典(比如 JSON 字符串),必须显式 json.loads() 或 dict() 转换{'x': {'a': 1}} | {'x': {'b': 2}} 结果是 {'x': {'b': 2}},不是 {'x': {'a': 1, 'b': 2}};需要深度合并请用 deepmerge 或手动递归d1 | d2 会触发 SyntaxError: invalid syntax,CI/CD 中容易漏测;若需兼容旧版本,只能退回 {**d1, **d2} 或封装工具函数真正难处理的从来不是怎么写,而是什么时候不该写——比如在热路径里反复用 | 合并大字典,或误以为它能自动 flatten 嵌套结构。这些地方一旦出问题,调试成本远高于多敲几行 update()。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8