| 和 |= 在 python 3.9–3.12 语法行为一致,但 3.12 对右操作数要求更严:必须是 collections.abc.mapping 实例,且 mypy 默认启用更严格协议检查,链式合并有小幅性能优化。

| 和 |= 在 Python 3.9 到 3.12 之间**语法、行为、类型支持完全一致**,没有新增或删减功能。你写在 3.9 能跑的合并表达式,在 3.12 里照样能跑,且结果一模一样。
但实际用起来,有几个关键点会因版本差异“悄悄变味”,得手动盯住。
Python 3.12 对右操作数的映射类型检查更严格
3.9 允许右操作数是任意实现 keys() 和 __getitem__() 的对象(比如自定义类),只要它“看起来像字典”就放行;3.12 则额外要求该对象必须是 collections.abc.Mapping 的实例(或显式注册为该抽象基类的子类)。
常见踩坑场景:
- 你自己写的轻量级配置类,只实现了
keys()和__getitem__(),没调用Mapping.register(MyConfig)→ 在 3.12 中d | my_config会抛TypeError: unsupported operand type,3.9 却能过 -
types.SimpleNamespace不是Mapping,哪怕它有__dict__,3.12 下d | ns直接失败,3.9 有时侥幸成功(取决于内部属性访问路径)
静态类型检查工具(如 mypy)在 3.12 环境下默认更保守
即使你代码能在 3.12 解释器里跑通,mypy 默认可能报错:
- mypy ≥ 1.10(对应 Python 3.12 支持)开始,默认启用更严格的协议检查 —— 它会要求右操作数必须满足
Mapping[K, V]协议,而不仅是鸭子类型 - 如果你传的是
dict[str, Any]给|,没问题;但传Dict[str, Any](旧式 typing.Dict)可能被标黄,提示“not a Mapping” - 解决办法不是降级 mypy,而是改用
typing.Mapping注解,或显式 cast:cast(Mapping[str, int], obj)
链式合并 d1 | d2 | d3 在 3.12 中性能略有优化,但不改变语义
CPython 3.12 对连续二元 | 做了少量内联优化,实测小字典(
真正要注意的是:链式合并仍是左结合、逐层浅拷贝 —— d1 | d2 | d3 等价于 (d1 | d2) | d3,不是一次性遍历三个字典。中间结果仍会创建临时字典,内存开销没变。
别指望 3.12 加了嵌套合并或 deep_merge 支持
所有资料里提到的“| 只做浅合并”,在 3.12 依然成立。官方没加、也没计划加递归合并能力。
如果看到有人声称 “Python 3.12 支持 d1 | d2 深度合并”,那要么是混淆了第三方库(如 deepmerge),要么是误读了 PEP 草案。
要深度合并,还是得自己写递归函数,或引入 deepmerge、mergedict 这类包 —— 它们和 | 是正交方案,不冲突也不替代。
| 表达式,CI 用 3.9 跑测试时一切正常;但若某天把 CI 升到 3.12,又没同步更新类型检查配置或自定义 Mapping 注册逻辑,就会突然 fail —— 不是语法错,而是类型兼容性静默降级。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











