How to merge dictionaries of dictionaries?

后端 未结 29 3406
渐次进展
渐次进展 2020-11-22 05:13

I need to merge multiple dictionaries, here\'s what I have for instance:

dict1 = {1:{\"a\":{A}}, 2:{\"b\":{B}}}

dict2 = {2:{\"c\":{C}}, 3:{\"d\":{D}}
         


        
29条回答
  •  猫巷女王i
    2020-11-22 05:54

    There's a slight problem with andrew cookes answer: In some cases it modifies the second argument b when you modify the returned dict. Specifically it's because of this line:

    if key in a:
        ...
    else:
        a[key] = b[key]
    

    If b[key] is a dict, it will simply be assigned to a, meaning any subsequent modifications to that dict will affect both a and b.

    a={}
    b={'1':{'2':'b'}}
    c={'1':{'3':'c'}}
    merge(merge(a,b), c) # {'1': {'3': 'c', '2': 'b'}}
    a # {'1': {'3': 'c', '2': 'b'}} (as expected)
    b # {'1': {'3': 'c', '2': 'b'}} <----
    c # {'1': {'3': 'c'}} (unmodified)
    

    To fix this, the line would have to be substituted with this:

    if isinstance(b[key], dict):
        a[key] = clone_dict(b[key])
    else:
        a[key] = b[key]
    

    Where clone_dict is:

    def clone_dict(obj):
        clone = {}
        for key, value in obj.iteritems():
            if isinstance(value, dict):
                clone[key] = clone_dict(value)
            else:
                clone[key] = value
        return
    

    Still. This obviously doesn't account for list, set and other stuff, but I hope it illustrates the pitfalls when trying to merge dicts.

    And for completeness sake, here is my version, where you can pass it multiple dicts:

    def merge_dicts(*args):
        def clone_dict(obj):
            clone = {}
            for key, value in obj.iteritems():
                if isinstance(value, dict):
                    clone[key] = clone_dict(value)
                else:
                    clone[key] = value
            return
    
        def merge(a, b, 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
                    else:
                        raise Exception('Conflict at `{path}\''.format(path='.'.join(path + [str(key)])))
                else:
                    if isinstance(b[key], dict):
                        a[key] = clone_dict(b[key])
                    else:
                        a[key] = b[key]
            return a
        return reduce(merge, args, {})
    

提交回复
热议问题