Python 3.9 定型 | 和 |= 行为,3.12 未改动:| 创建新字典,|= 就地更新;均只接受映射类型,不支持嵌套合并或 None 安全;解包和 update() 同样保持严格类型校验与原有语义。

| 和 |= 在 3.9 引入,3.12 未改动——语法层面没演进,但语义和容错边界更清晰了
Python 3.9 就定型了 | 和 |= 的行为
这两个操作符不是“逐步完善”,而是 3.9 一次性定义清楚的:| 创建新字典,|= 就地更新。3.10、3.11、3.12 都没改它们的语义或签名。你写 dict_a | dict_b 在 3.9 和 3.12 下结果完全一致。
常见误解是以为后续版本加了“嵌套合并”或“None 安全支持”——没有。所有版本都坚持一个原则:只接受映射类型(dict 或 Mapping 子类),其他一律报 TypeError。
-
dict_a | None→TypeError: unsupported operand type(s) for |: 'dict' and 'NoneType' -
dict_a | []→ 同样报错,不是静默跳过 - 想兼容空值?得自己处理:
dict_a | (dict_b or {}),但注意{}是 falsy,or会把它转成空字典
** 解包在 3.12 里依然不支持嵌套合并
很多人误以为新版 Python 让 {**a, **b} 自动递归合并嵌套字典——它从来不会。3.12 还是简单覆盖:遇到同名键,直接用 b 的整个值替换 a 的,不管那个值是不是字典。
立即学习“Python免费学习笔记(深入)”;
比如:
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
dict_a = {'x': {'y': 1}}
dict_b = {'x': {'z': 2}}
merged = {**dict_a, **dict_b} # 结果是 {'x': {'z': 2}},不是 {'x': {'y': 1, 'z': 2}}这个限制从 3.5 开始就没变过。3.12 只强化了错误提示:如果某个解包项是 None,报错信息更直白——TypeError: 'NoneType' object is not a mapping,而不是模糊的 TypeError。
update() 在 3.12 仍返回 None,且不接受非映射类型
dict.update() 的行为在所有版本里都一样:就地修改、返回 None、只认映射类型或可迭代的键值对。3.12 没放宽校验,也不会自动把 None 当空字典处理。
-
dict_a.update(None)→TypeError,不是忽略 -
dict_a.update([('a', 1), ('b', 2)])→ 支持,这是它比|更灵活的地方 - 想链式调用?不行:
dict_a.update(dict_b).keys()会报AttributeError,因为update()返回None
真正值得留意的,是“你以为安全的写法其实有坑”
比如用 dict_a | (dict_b or {}) 处理可能为 None 的 dict_b,看似稳妥,但若 dict_b 是空字典 {},or 也会触发右侧,变成 dict_a | {}——这没问题;可如果 dict_b 是 0、False 或空列表 [],它们也是 falsy,or 同样会选 {},导致你丢失原始意图。
更健壮的做法是显式判断类型:
def safe_merge(a, b):
if not isinstance(b, dict):
b = {}
return a | b或者直接用 isinstance(b, Mapping)(需导入 from collections.abc import Mapping)——这才是 3.12 真正鼓励的写法:不靠隐式转换,靠明确类型契约。

















