更新深度不一的嵌套字典的值
我想找一种方法,把字典 dictionary1
的内容更新为字典 update
的内容,但不想覆盖掉 levelA
的部分。
dictionary1 = {
"level1": {
"level2": {"levelA": 0, "levelB": 1}
}
}
update = {
"level1": {
"level2": {"levelB": 10}
}
}
dictionary1.update(update)
print(dictionary1)
{
"level1": {
"level2": {"levelB": 10}
}
}
我知道,使用更新操作会删除 level2
的值,因为它是在更新最底层的 level1
。
考虑到 dictionary1
和 update
的长度可能不一样,我该怎么处理这个问题呢?
31 个回答
37
我花了一些时间才搞明白这个问题,不过多亏了@Alex的帖子,他帮我填补了我缺失的部分。不过,我遇到了一个问题,就是如果递归的dict
里面有一个值是list
的话,所以我想分享一下,并扩展一下他的回答。
import collections
def update(orig_dict, new_dict):
for key, val in new_dict.iteritems():
if isinstance(val, collections.Mapping):
tmp = update(orig_dict.get(key, { }), val)
orig_dict[key] = tmp
elif isinstance(val, list):
orig_dict[key] = (orig_dict.get(key, []) + val)
else:
orig_dict[key] = new_dict[key]
return orig_dict
393
@FM的回答有个大致的思路是对的,也就是用递归的方法,但代码写得有点奇怪,而且至少有一个错误。我建议可以这样做:
Python 2:
import collections
def update(d, u):
for k, v in u.iteritems():
if isinstance(v, collections.Mapping):
d[k] = update(d.get(k, {}), v)
else:
d[k] = v
return d
Python 3:
import collections.abc
def update(d, u):
for k, v in u.items():
if isinstance(v, collections.abc.Mapping):
d[k] = update(d.get(k, {}), v)
else:
d[k] = v
return d
这个错误出现在“更新”中有一个 k
和 v
的项,其中 v
是一个 dict
(字典),而 k
在要更新的字典中原本并不是一个键。@FM的代码在处理这个更新时“跳过”了这一部分(因为它在一个空的新 dict
上执行这个操作,而这个新字典没有被保存或返回,等递归调用结束后就丢失了)。
我其他的改动比较小:当 .get
可以更快更简洁地完成同样的工作时,就没有必要使用 if
/else
的结构,而 isinstance
最好应用于抽象基类(而不是具体的类),这样更通用。