This is actually quite tricky – particularly if you want a useful error message when things are inconsistent, while correctly accepting duplicate but consistent entries (something no other answer here does..)
Assuming you don’t have huge numbers of entries, a recursive function is easiest:
def merge(a, b, path=None):
"merges b into a"
if path is None: path = []
for key in b:
if key in a:
if isinstance(a[key], dict) and isinstance(b[key], dict):
merge(a[key], b[key], path + [str(key)])
elif a[key] == b[key]:
pass # same leaf value
else:
raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
else:
a[key] = b[key]
return a
# works
print(merge({1:{"a":"A"},2:{"b":"B"}}, {2:{"c":"C"},3:{"d":"D"}}))
# has conflict
merge({1:{"a":"A"},2:{"b":"B"}}, {1:{"a":"A"},2:{"b":"C"}})
note that this mutates a
– the contents of b
are added to a
(which is also returned). If you want to keep a
you could call it like merge(dict(a), b)
.
agf pointed out (below) that you may have more than two dicts, in which case you can use:
reduce(merge, [dict1, dict2, dict3...])
where everything will be added to dict1
.
Note: I edited my initial answer to mutate the first argument; that makes the “reduce” easier to explain
PS: In python 3, you will also need from functools import reduce