我正在寻找一种更新dict dictionary1的方法,用dict update更新内容覆盖levelA
dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}}}
update={'level1':{'level2':{'levelB':10}}}
dictionary1.update(update)
print dictionary1
{'level1': {'level2': {'levelB': 10}}}
我知道更新会删除level2中的值,因为它正在更新最低密钥级别1。
我可以解决这个问题,因为dictionary1和update可以有任何长度吗?
答案 0 :(得分:221)
@ 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
Python 3:
import collections
def update(d, u):
for k, v in u.items():
if isinstance(v, collections.Mapping):
d[k] = update(d.get(k, {}), v)
else:
d[k] = v
return d
当“更新”包含k
,v
项v
为dict
且k
原本不是关键字时,会显示错误在更新的字典中 - @ FM的代码“跳过”这部分更新(因为它在空的新dict
上执行它,它不会保存或返回任何地方,只是在递归调用返回时丢失)。
我的其他更改很小:当if
更快更干净地执行相同的工作时,else
/ .get
构造没有理由,isinstance
最佳应用为了一般性而抽象基类(不是具体的)。
答案 1 :(得分:19)
对我说了一点,但多亏了@ 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
答案 2 :(得分:15)
@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}}})
答案 3 :(得分:9)
与接受的解决方案相同,但更清晰的变量命名,文档字符串,并修复了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
作为值不会覆盖的错误。
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}}
以下是一些测试用例:
charlatan.utils
此功能位于function showVictoryMessage(playerName)
local message = Instance.new("Message")
message.Text = playerName .." has won!"
message.Parent = game.Workspace
wait (2)
message.Destroy()
end
中的charlatan包中。
答案 4 :(得分:6)
@Alex's answer的微小改进,可以更新不同深度的词典,并限制更新深入到原始嵌套词典的深度(但更新词典深度不受限制)。只有少数案例经过测试:
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
答案 5 :(得分:5)
这是一个不可变版本的递归字典合并,以防任何人需要它。
基于@Alex Martelli的answer。
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
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
答案 6 :(得分:3)
这个问题很旧,但是我在寻找“深度合并”解决方案时就落在了这里。上面的答案启发了接下来的事情。我最终写了我自己的,因为我测试的所有版本中都有错误。错过的关键点是,在两个输入字典的任意深度处,对于某个键k,当d [k]或u [k]不是 字典错误时,决策树。 / p>
此外,此解决方案不需要递归,这与SELECT ((date_trunc('day',nowAtShopLocation)+"OpenAt"::time, date_trunc('day',nowAtShopLocation)+"CloseAt"::time) OVERLAPS(nowAtShopLocation,nowAtShopLocation)) and EXTRACT (ISODOW FROM nowAtShopLocation) <6
from (
select *,now() AT TIME ZONE 'UTC'+(EXTRACT(TIMEZONE_HOUR FROM "OpenAt")||' hour')::interval nowAtShopLocation from your_table
) a
的工作方式更加对称,并返回dict.update()
。
None
答案 7 :(得分:2)
更新@Alex Martelli的答案,修复他的代码中的错误,使解决方案更加强大:
def update_dict(d, u):
for k, v in u.items():
if isinstance(v, collections.Mapping):
default = v.copy()
default.clear()
r = update_dict(d.get(k, default), v)
d[k] = r
else:
d[k] = v
return d
关键是我们经常要在递归时创建相同类型,因此我们在这里使用v.copy().clear()
但不使用{}
。如果dict
此类型collections.defaultdict
可以有不同类型的default_factory
,则此功能特别有用。
另请注意u.iteritems()
中的u.items()
已更改为Python3
。
答案 8 :(得分:2)
下面的代码应该以正确的方式解决@Alex Martelli的答案中的update({'k1': 1}, {'k1': {'k2': 2}})
问题。
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
答案 9 :(得分:2)
我使用@Alex Martelli建议的解决方案,但它失败了
TypeError 'bool' object does not support item assignment
当两个词典在某种程度上的数据类型不同时。
如果在同一级别,字典d
的元素只是一个标量(即。Bool
)而字典u
的元素仍然是字典,则重新分配失败,因为没有字典赋值可以转换为标量(如True[k]
)。
一个附加条件修复了:
from collections import Mapping
def update_deep(d, u):
for k, v in u.items():
# this condition handles the problem
if not isinstance(d, Mapping):
d = u
elif isinstance(v, Mapping):
r = update_deep(d.get(k, {}), v)
d[k] = r
else:
d[k] = u[k]
return d
答案 10 :(得分:2)
在这两个答案中,作者似乎都理解更新存储在字典中的对象的概念,甚至不是迭代字典项(而不是键)。所以我不得不写一个没有做无意义的重言式词典存储和检索的。 假设dicts存储其他dicts或简单类型。
def update_nested_dict(d, other):
for k, v in other.items():
if isinstance(v, collections.Mapping):
d_v = d.get(k)
if isinstance(d_v, collections.Mapping):
update_nested_dict(d_v, v)
else:
d[k] = v.copy()
else:
d[k] = v
甚至更简单的任何一种工作:
def update_nested_dict(d, other):
for k, v in other.items():
d_v = d.get(k)
if isinstance(v, collections.Mapping) and isinstance(d_v, collections.Mapping):
update_nested_dict(d_v, v)
else:
d[k] = deepcopy(v) # or d[k] = v if you know what you're doing
答案 11 :(得分:1)
可能是你偶然发现了一个非标准词典,就像我今天一样,没有iteritems-Attribute。 在这种情况下,很容易将这种类型的字典解释为标准字典。 E.g:
import collections
def update(orig_dict, new_dict):
for key, val in dict(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[key] + val)
else:
orig_dict[key] = new_dict[key]
return orig_dict
import multiprocessing
d=multiprocessing.Manager().dict({'sample':'data'})
u={'other': 1234}
x=update(d, u)
x.items()
答案 12 :(得分:1)
我知道这个问题已经很老了,但是当我不得不更新嵌套字典时,仍然发布了我的工作。我们可以使用dict通过python中的引用传递的事实 假定键的路径是已知的并且是点分隔的。外汇,如果我们有一个名为data的字典:
{
"log_config_worker": {
"version": 1,
"root": {
"handlers": [
"queue"
],
"level": "DEBUG"
},
"disable_existing_loggers": true,
"handlers": {
"queue": {
"queue": null,
"class": "myclass1.QueueHandler"
}
}
},
"number_of_archived_logs": 15,
"log_max_size": "300M",
"cron_job_dir": "/etc/cron.hourly/",
"logs_dir": "/var/log/patternex/",
"log_rotate_dir": "/etc/logrotate.d/"
}
我们要更新队列类,密钥的路径为-log_config_worker.handlers.queue.class
我们可以使用以下函数来更新值:
def get_updated_dict(dict_to_update, path, value):
obj = dict_to_update
key_list = path.split(".")
for k in key_list[:-1]:
obj = obj[k]
obj[key_list[-1]] = value
get_updated_dict(data, "log_config_worker.handlers.queue.class", "myclass2.QueueHandler")
这将正确更新字典。
答案 13 :(得分:1)
只需使用python-benedict
(我做到了),它就有一个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}}}
安装:pip install python-benedict
答案 14 :(得分:0)
您可以尝试一下,它可以与列表一起使用,并且很纯净:
def update_keys(newd, dic, mapping):
def upsingle(d,k,v):
if k in mapping:
d[mapping[k]] = v
else:
d[k] = v
for ekey, evalue in dic.items():
upsingle(newd, ekey, evalue)
if type(evalue) is dict:
update_keys(newd, evalue, mapping)
if type(evalue) is list:
upsingle(newd, ekey, [update_keys({}, i, mapping) for i in evalue])
return newd
答案 15 :(得分:0)
我建议将{}
替换为type(v)()
,以便传播存储在u
中但没有d
的任何dict子类的对象类型。例如,这将保留诸如collections.OrderedDict:
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, type(v)()), 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, type(v)()), v)
else:
d[k] = v
return d
答案 16 :(得分:0)
def update(value, nvalue):
if not isinstance(value, dict) or not isinstance(nvalue, dict):
return nvalue
for k, v in nvalue.items():
value.setdefault(k, dict())
if isinstance(v, dict):
v = update(value[k], v)
value[k] = v
return value
使用dict
或collections.Mapping
答案 17 :(得分:0)
感谢hobs对Alex's answer的评论。确实self.balance
会导致update({'k1': 1}, {'k1': {'k2': 2}})
我们应该在函数开始时检查输入值的类型。因此,我建议使用以下功能,该功能可以解决这个(以及其他)问题。
Python 3:
TypeError: 'int' object does not support item assignment.
答案 18 :(得分:0)
我做了一个简单的函数,其中您给键,新值和字典作为输入,它递归地用值更新它:
def update(key,value,dictionary):
if key in dictionary.keys():
dictionary[key] = value
return
dic_aux = []
for val_aux in dictionary.values():
if isinstance(val_aux,dict):
dic_aux.append(val_aux)
for i in dic_aux:
update(key,value,i)
for [key2,val_aux2] in dictionary.items():
if isinstance(val_aux2,dict):
dictionary[key2] = val_aux2
dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}}}
update('levelB',10,dictionary1)
print(dictionary1)
#output: {'level1': {'level2': {'levelA': 0, 'levelB': 10}}}
希望它能回答。
答案 19 :(得分:0)
如果您碰巧正在使用 pydantic(很棒的库,顺便说一句),您可以使用它的一种实用方法:
from pydantic.utils import deep_update
dictionary1 = deep_update(dictionary1, update)
答案 20 :(得分:0)
新问 如何通过钥匙链
dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}},'anotherLevel1':{'anotherLevel2':{'anotherLevelA':0,'anotherLevelB':1}}}
update={'anotherLevel1':{'anotherLevel2':1014}}
dictionary1.update(update)
print dictionary1
{'level1':{'level2':{'levelA':0,'levelB':1}},'anotherLevel1':{'anotherLevel2':1014}}
答案 21 :(得分:0)
使用递归的另一种方法:
def updateDict(dict1,dict2):
keys1 = list(dict1.keys())
keys2= list(dict2.keys())
keys2 = [x for x in keys2 if x in keys1]
for x in keys2:
if (x in keys1) & (type(dict1[x]) is dict) & (type(dict2[x]) is dict):
updateDict(dict1[x],dict2[x])
else:
dict1.update({x:dict2[x]})
return(dict1)
答案 22 :(得分:0)
是的!还有另一种解决方案。我的解决方案在要检查的键上有所不同。
在所有其他解决方案中,我们仅查看dict_b
中的键。但是这里我们看一下两个字典的结合。
随心所欲
def update_nested(dict_a, dict_b):
set_keys = set(dict_a.keys()).union(set(dict_b.keys()))
for k in set_keys:
v = dict_a.get(k)
if isinstance(v, dict):
new_dict = dict_b.get(k, None)
if new_dict:
update_nested(v, new_dict)
else:
new_value = dict_b.get(k, None)
if new_value:
dict_a[k] = new_value
答案 23 :(得分:0)
如果要用数组替换“完全嵌套的字典”,则可以使用以下代码段:
它将用“ new_value”替换任何“ old_value”。它大致在对字典进行深度优先的重建。它甚至可以与作为第一级输入参数的List或Str / int一起使用。
def update_values_dict(original_dict, future_dict, old_value, new_value):
# Recursively updates values of a nested dict by performing recursive calls
if isinstance(original_dict, Dict):
# It's a dict
tmp_dict = {}
for key, value in original_dict.items():
tmp_dict[key] = update_values_dict(value, future_dict, old_value, new_value)
return tmp_dict
elif isinstance(original_dict, List):
# It's a List
tmp_list = []
for i in original_dict:
tmp_list.append(update_values_dict(i, future_dict, old_value, new_value))
return tmp_list
else:
# It's not a dict, maybe a int, a string, etc.
return original_dict if original_dict != old_value else new_value
答案 24 :(得分:-1)
如果您想要单线:
{**dictionary1, **{'level1':{**dictionary1['level1'], **{'level2':{**dictionary1['level1']['level2'], **{'levelB':10}}}}}}
答案 25 :(得分:-1)
这有点偏向但你真的需要嵌套字典吗?根据问题,有时平字典可能就足够了......并且看起来很好:
>>> dict1 = {('level1','level2','levelA'): 0}
>>> dict1['level1','level2','levelB'] = 1
>>> update = {('level1','level2','levelB'): 10}
>>> dict1.update(update)
>>> print dict1
{('level1', 'level2', 'levelB'): 10, ('level1', 'level2', 'levelA'): 0}