Python标准库无deepmerge,dict.update()仅浅合并;推荐用deepmerge库或手写函数,需处理字典递归、列表拼接/去重、None校验及类型守卫,避免无谓拷贝。

deepmerge 函数不存在,得自己实现或选对第三方库
Python 标准库没有 deepmerge,dict.update() 只做浅合并,遇到嵌套字典会直接覆盖而非递归合并。常见错误是写个递归函数但没处理列表、None、不可变类型等边界情况,结果在合并 {"a": [1, 2]} 和 {"a": [3]} 时要么报错,要么丢数据。
推荐两个务实路径:
- 轻量需求:用
deepmerge第三方包(注意不是deep-merge),安装后调用merge()即可,默认策略是“右值优先”,支持自定义冲突处理器 - 不想加依赖:手写一个最小可用版本,重点守住三类分支:字典→递归合并;列表→默认拼接(或去重/替换,按需选);其余类型→以右边为准(可配)
手动实现时必须显式处理列表合并策略
多数人卡在列表上:是拼接、去重、还是按索引合并?Python 不像 JavaScript 有明确的 Array.prototype.concat 语义。默认行为应由你定义,不能靠“看起来合理”。
示例片段(仅核心逻辑):
立即学习“Python免费学习笔记(深入)”;
def deep_merge(left, right):
if isinstance(left, dict) and isinstance(right, dict):
result = left.copy()
for k, v in right.items():
if k in result and isinstance(result[k], dict) and isinstance(v, dict):
result[k] = deep_merge(result[k], v)
else:
result[k] = v # 字典里非字典值,直接覆盖
return result
elif isinstance(left, list) and isinstance(right, list):
return left + right # 简单拼接;如需去重:list(dict.fromkeys(left + right))
else:
return right # 非容器类型,右值胜出
注意:left + right 对字符串也成立,但通常你不希望 "ab" 和 "cd" 合成 "abcd" —— 所以实际使用前务必加类型守卫,比如只对 list 做拼接,其他标量一律覆盖。
创建 Flask + Vue.js 全栈项目,采用单端口架构、uv 依赖管理、结构化日志和生产级前端设计。当用户要求以下操作时触发:创建新的 Flask+Vue 项目、搭建 Web 应用、构建全栈 Python+Vue 应用、初始化 Flask Web 项目、设置单端口 Flask 前后端项目。触发词:新建项目、创建项目、搭建项目、Flask项目、Vue项目。
遇到 None 或缺失键时,合并逻辑容易崩
真实数据常含 None,比如 API 返回 {"user": {"profile": None}} 和 {"user": {"settings": {"theme": "dark"}}}。若不提前判断,递归进 None 会触发 AttributeError: 'NoneType' object has no attribute 'items'。
安全做法是统一前置校验:
- 若
left为None,直接返回right(无论right是什么) - 若
right为None,返回left - 键存在但值为
None(如left["x"] = None),按策略决定:保留None、跳过、还是用right的对应值兜底
别依赖“数据干净”——混合类型嵌套字典的典型场景就是脏数据清洗,防御性编码是底线。
性能敏感时避免无谓递归和拷贝
深合并本质是树遍历,但每次 .copy() 或构造新字典都会触发内存分配。万级嵌套或大体积字典下,left.copy() 可能比合并逻辑本身更耗时。
优化点很具体:
- 用
collections.ChainMap临时包装多层字典(只读场景),零拷贝 - 原地合并(
mutate=True参数):直接修改left,省掉copy,但调用方需清楚副作用 - 对超大列表,避免
left + right(生成新对象),改用left.extend(right)(原地)
真正难的不是写出能跑的合并函数,而是说清“这个 None 是该留还是该删”、“这两个同名列表到底算配置叠加还是完全替换”。业务语义不清,代码再工整也没用。

















