我正在寻找一种方法来更新dict dictionary1与dict更新的内容wihout覆盖levelA
dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}}}
update={'level1':{'level2':{'levelB':10}}}
dictionary1.update(update)
print dictionary1
{'level1': {'level2': {'levelB': 10}}}
Run Code Online (Sandbox Code Playgroud)
我知道更新会删除level2中的值,因为它正在更新最低密钥level1.
鉴于dictionary1和update可以有任何长度,我怎么能解决这个问题呢?
Ale*_*lli 246
@ FM的答案有正确的总体思路,即递归解决方案,但有些特殊的编码和至少一个bug.我推荐,相反:
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
该错误时显示"更新"有k,v项目在那里v是dict和k最初不是被更新在字典中的关键- @ FM代码"跳过"更新的这一部分(因为它执行它的新的空dict其没有保存或返回任何地方,只是在递归调用返回时丢失).
我的其他更改是次要的:没有理由if/ elseconstruct什么时候.get同一个工作更快更干净,并且isinstance最好应用于抽象基类(不是具体的)以获得通用性.
kep*_*ler 63
如果您碰巧正在使用pydantic(很棒的库,顺便说一句),您可以使用它的实用方法之一:
from pydantic.utils import deep_update
dictionary1 = deep_update(dictionary1, update)
Run Code Online (Sandbox Code Playgroud)
更新:对 code 的引用,如@Jorgu所指出的。如果不需要安装 pydantic,只要有足够的许可证兼容性,代码就足够短,可以复制。
小智 22
对我说了一点,但多亏了@ Alex的帖子,他填补了我所缺少的空白.但是,如果递归中的值dict恰好是a ,我遇到了一个问题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
Run Code Online (Sandbox Code Playgroud)
bsc*_*can 16
@Alex的答案很好,但是当用字典替换整数等元素时不起作用,例如update({'foo':0},{'foo':{'bar':1}}).此更新解决了它:
import collections
def update(d, u):
for k, v in u.iteritems():
if isinstance(d, collections.Mapping):
if isinstance(v, collections.Mapping):
r = update(d.get(k, {}), v)
d[k] = r
else:
d[k] = u[k]
else:
d = {k: u[k]}
return d
update({'k1': 1}, {'k1': {'k2': {'k3': 3}}})
Run Code Online (Sandbox Code Playgroud)
cha*_*lax 12
与接受的解决方案相同的解决方案,但更清晰的变量命名,docstring,并修复了一个错误,其中{}值不会覆盖.
import collections
def deep_update(source, overrides):
"""
Update a nested dictionary or similar mapping.
Modify ``source`` in place.
"""
for key, value in overrides.iteritems():
if isinstance(value, collections.Mapping) and value:
returned = deep_update(source.get(key, {}), value)
source[key] = returned
else:
source[key] = overrides[key]
return source
Run Code Online (Sandbox Code Playgroud)
以下是一些测试用例:
def test_deep_update():
source = {'hello1': 1}
overrides = {'hello2': 2}
deep_update(source, overrides)
assert source == {'hello1': 1, 'hello2': 2}
source = {'hello': 'to_override'}
overrides = {'hello': 'over'}
deep_update(source, overrides)
assert source == {'hello': 'over'}
source = {'hello': {'value': 'to_override', 'no_change': 1}}
overrides = {'hello': {'value': 'over'}}
deep_update(source, overrides)
assert source == {'hello': {'value': 'over', 'no_change': 1}}
source = {'hello': {'value': 'to_override', 'no_change': 1}}
overrides = {'hello': {'value': {}}}
deep_update(source, overrides)
assert source == {'hello': {'value': {}, 'no_change': 1}}
source = {'hello': {'value': {}, 'no_change': 1}}
overrides = {'hello': {'value': 2}}
deep_update(source, overrides)
assert source == {'hello': {'value': 2, 'no_change': 1}}
Run Code Online (Sandbox Code Playgroud)
这个功能在charlatan包中可用charlatan.utils.
这个问题很老,但我在寻找“深度合并”解决方案时来到这里。上面的答案启发了接下来的内容。我最终写了自己的,因为我测试的所有版本都存在错误。错过的关键点是,在两个输入字典的任意深度,对于某些键 k,当 d[k] 或 u[k]不是字典时,决策树是错误的。
此外,此解决方案不需要递归,这与dict.update()工作原理更加对称,并返回None.
import collections
def deep_merge(d, u):
"""Do a deep merge of one dict into another.
This will update d with values in u, but will not delete keys in d
not found in u at some arbitrary depth of d. That is, u is deeply
merged into d.
Args -
d, u: dicts
Note: this is destructive to d, but not u.
Returns: None
"""
stack = [(d,u)]
while stack:
d,u = stack.pop(0)
for k,v in u.items():
if not isinstance(v, collections.Mapping):
# u[k] is not a dict, nothing to merge, so just set it,
# regardless if d[k] *was* a dict
d[k] = v
else:
# note: u[k] is a dict
if k not in d:
# add new key into d
d[k] = v
elif not isinstance(d[k], collections.Mapping):
# d[k] is not a dict, so just set it to u[k],
# overriding whatever it was
d[k] = v
else:
# both d[k] and u[k] are dicts, push them on the stack
# to merge
stack.append((d[k], v))
Run Code Online (Sandbox Code Playgroud)
对@ Alex的答案进行了一些小改进,可以更新不同深度的字典,并限制更新深入到原始嵌套字典中的深度(但更新字典深度不受限制).只有少数案例经过测试:
def update(d, u, depth=-1):
"""
Recursively merge or update dict-like objects.
>>> update({'k1': {'k2': 2}}, {'k1': {'k2': {'k3': 3}}, 'k4': 4})
{'k1': {'k2': {'k3': 3}}, 'k4': 4}
"""
for k, v in u.iteritems():
if isinstance(v, Mapping) and not depth == 0:
r = update(d.get(k, {}), v, depth=max(depth - 1, -1))
d[k] = r
elif isinstance(d, Mapping):
d[k] = u[k]
else:
d = {k: u[k]}
return d
Run Code Online (Sandbox Code Playgroud)
如果有人需要,这是递归字典合并的不可变版本。
基于@Alex Martelli的答案。
Python 2.x:
import collections
from copy import deepcopy
def merge(dict1, dict2):
''' Return a new dictionary by merging two dictionaries recursively. '''
result = deepcopy(dict1)
for key, value in dict2.iteritems():
if isinstance(value, collections.Mapping):
result[key] = merge(result.get(key, {}), value)
else:
result[key] = deepcopy(dict2[key])
return result
Run Code Online (Sandbox Code Playgroud)
Python 3.x:
import collections
from copy import deepcopy
def merge(dict1, dict2):
''' Return a new dictionary by merging two dictionaries recursively. '''
result = deepcopy(dict1)
for key, value in dict2.items():
if isinstance(value, collections.Mapping):
result[key] = merge(result.get(key, {}), value)
else:
result[key] = deepcopy(dict2[key])
return result
Run Code Online (Sandbox Code Playgroud)
只需使用python-benedict (I did it),它就有一个merge(deepupdate) 实用程序方法和许多其他方法。它适用于 python 2 / python 3,并且经过了很好的测试。
from benedict import benedict
dictionary1=benedict({'level1':{'level2':{'levelA':0,'levelB':1}}})
update={'level1':{'level2':{'levelB':10}}}
dictionary1.merge(update)
print(dictionary1)
# >> {'level1':{'level2':{'levelA':0,'levelB':10}}}
Run Code Online (Sandbox Code Playgroud)
安装: pip install python-benedict
文档:https : //github.com/fabiocaccamo/python-benedict
注意:我是这个项目的作者
下面的代码应该以update({'k1': 1}, {'k1': {'k2': 2}})正确的方式解决@Alex Martelli 的答案中的问题。
def deepupdate(original, update):
"""Recursively update a dict.
Subdict's won't be overwritten but also updated.
"""
if not isinstance(original, abc.Mapping):
return update
for key, value in update.items():
if isinstance(value, abc.Mapping):
original[key] = deepupdate(original.get(key, {}), value)
else:
original[key] = value
return original
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
72175 次 |
| 最近记录: |