我需要合并多个字典,这是我有例如:
dict1 = {1:{"a":{A}}, 2:{"b":{B}}}
dict2 = {2:{"c":{C}}, 3:{"d":{D}}}
A、B、C和D是树的叶子,比如{"info1":"value", "info2":"value2"}
字典的级别(深度)未知,可能是{2:{"c":{"z":{"y":{c}}}}}
在我的例子中,它表示一个目录/文件结构,节点是文档,叶子是文件。
我想将它们合并得到:
dict3 = {1:{"a":{A}}, 2:{"b":{B},"c":{C}}, 3:{"d":{D}}}
我不确定如何用Python轻松做到这一点。
class Utils(object):
"""
>>> a = { 'first' : { 'all_rows' : { 'pass' : 'dog', 'number' : '1' } } }
>>> b = { 'first' : { 'all_rows' : { 'fail' : 'cat', 'number' : '5' } } }
>>> Utils.merge_dict(b, a) == { 'first' : { 'all_rows' : { 'pass' : 'dog', 'fail' : 'cat', 'number' : '5' } } }
True
>>> main = {'a': {'b': {'test': 'bug'}, 'c': 'C'}}
>>> suply = {'a': {'b': 2, 'd': 'D', 'c': {'test': 'bug2'}}}
>>> Utils.merge_dict(main, suply) == {'a': {'b': {'test': 'bug'}, 'c': 'C', 'd': 'D'}}
True
"""
@staticmethod
def merge_dict(main, suply):
"""
获取融合的字典,以main为主,suply补充,冲突时以main为准
:return:
"""
for key, value in suply.items():
if key in main:
if isinstance(main[key], dict):
if isinstance(value, dict):
Utils.merge_dict(main[key], value)
else:
pass
else:
pass
else:
main[key] = value
return main
if __name__ == '__main__':
import doctest
doctest.testmod()
这个版本的函数将处理N个字典,并且只处理字典——不能传递不恰当的参数,否则将引发TypeError。合并本身解释了键冲突,它不是覆盖来自合并链下的字典的数据,而是创建一组值并追加到该值;没有数据丢失。
它可能不是页面上最有效的,但它是最彻底的,当你合并2到N字典时,你不会丢失任何信息。
def merge_dicts(*dicts):
if not reduce(lambda x, y: isinstance(y, dict) and x, dicts, True):
raise TypeError, "Object in *dicts not of type dict"
if len(dicts) < 2:
raise ValueError, "Requires 2 or more dict objects"
def merge(a, b):
for d in set(a.keys()).union(b.keys()):
if d in a and d in b:
if type(a[d]) == type(b[d]):
if not isinstance(a[d], dict):
ret = list({a[d], b[d]})
if len(ret) == 1: ret = ret[0]
yield (d, sorted(ret))
else:
yield (d, dict(merge(a[d], b[d])))
else:
raise TypeError, "Conflicting key:value type assignment"
elif d in a:
yield (d, a[d])
elif d in b:
yield (d, b[d])
else:
raise KeyError
return reduce(lambda x, y: dict(merge(x, y)), dicts[1:], dicts[0])
print merge_dicts({1:1,2:{1:2}},{1:2,2:{3:1}},{4:4})
输出:{1:[1,2],2:{1:2,3:1},4:4}
def m(a,b):
aa = {
k : dict(a.get(k,{}), **v) for k,v in b.items()
}
aap = print(aa)
return aap
d1 = {1:{"a":"A"}, 2:{"b":"B"}}
d2 = {2:{"c":"C"}, 3:{"d":"D"}}
dict1 = {1:{"a":{1}}, 2:{"b":{2}}}
dict2 = {2:{"c":{222}}, 3:{"d":{3}}}
m(d1,d2)
m(dict1,dict2)
"""
Output :
{2: {'b': 'B', 'c': 'C'}, 3: {'d': 'D'}}
{2: {'b': {2}, 'c': {222}}, 3: {'d': {3}}}
"""
我有一个迭代的解决方案-工作得更好的大字典&很多(例如jsons等):
import collections
def merge_dict_with_subdicts(dict1: dict, dict2: dict) -> dict:
"""
similar behaviour to builtin dict.update - but knows how to handle nested dicts
"""
q = collections.deque([(dict1, dict2)])
while len(q) > 0:
d1, d2 = q.pop()
for k, v in d2.items():
if k in d1 and isinstance(d1[k], dict) and isinstance(v, dict):
q.append((d1[k], v))
else:
d1[k] = v
return dict1
注意,这将使用d2中的值来覆盖d1,以防它们都不是字典。(与python的dict.update()相同)
一些测试:
def test_deep_update():
d = dict()
merge_dict_with_subdicts(d, {"a": 4})
assert d == {"a": 4}
new_dict = {
"b": {
"c": {
"d": 6
}
}
}
merge_dict_with_subdicts(d, new_dict)
assert d == {
"a": 4,
"b": {
"c": {
"d": 6
}
}
}
new_dict = {
"a": 3,
"b": {
"f": 7
}
}
merge_dict_with_subdicts(d, new_dict)
assert d == {
"a": 3,
"b": {
"c": {
"d": 6
},
"f": 7
}
}
# test a case where one of the dicts has dict as value and the other has something else
new_dict = {
'a': {
'b': 4
}
}
merge_dict_with_subdicts(d, new_dict)
assert d['a']['b'] == 4
我已经测试了大约1200个字典——这种方法花了0.4秒,而递归的解决方案花了2.5秒。