2

I am looking for feedback on my Python code. I am trying to merge two dictionaries. One of the dictionaries controls the structure and the default values, the second dictionary will overwrite the default values when applicable.

Please note that I am looking for the following behaviour:

  • keys only present in the other dict should not be added
  • nested dicts should be taken into account

I wrote this simple function:

def merge_dicts(base_dict, other_dict):
    """ Merge two dicts

    Ensure that the base_dict remains as is and overwrite info from other_dict
    """
    out_dict = dict()
    for key, value in base_dict.items():
        if key not in other_dict:
            # simply use the base
            nvalue = value
        elif isinstance(other_dict[key], type(value)):
            if isinstance(value, type({})):
                # a new dict myst be recursively merged
                nvalue = merge_dicts(value, other_dict[key])
            else:
                # use the others' value
                nvalue = other_dict[key]
        else:
            # error due to difference of type
            raise TypeError('The type of key {} should be {} (currently is {})'.format(
                key,
                type(value),
                type(other_dict[key]))
            )
        out_dict[key] = nvalue
    return out_dict

I am sure this can be done more beautifully/pythonic.

Patrick
  • 303
  • 4
  • 13

2 Answers2

4

If you're using python 3.5 or later you can simply do:

merged_dict = {**base_dict, **other_dict}

In case you're using any prior version you can do it with the update method:

merged_dict = {}
merged_dict.update(base_dict)
merged_dict.update(other_dict)

For more information about it you can check The Idiomatic Way to Merge Dictionaries in Python

Carlos Afonso
  • 1,927
  • 1
  • 12
  • 22
2

"Pythonicness" is a hard measure to assess, but here is my take on it:

def merge_dicts(base_dict, other_dict):
    """ Merge two dicts

    Ensure that the base_dict remains as is and overwrite info from other_dict
    """
    if other_dict is None:
        return base_dict
    t = type(base_dict)
    if type(other_dict) != t:
        raise TypeError("Mismatching types: {} and {}."
                        .format(t, type(other_dict)))
    if not issubclass(t, dict):
        return other_dict
    return {k: merge_dicts(v, other_dict.get(k)) for k, v in base_dict.items()}

Example:

merge_dicts({"a":2, "b":{"b1": 5, "b2": 7}}, {"b": {"b1": 9}})
>>> {'a': 2, 'b': {'b1': 9, 'b2': 7}}
jdehesa
  • 58,456
  • 7
  • 77
  • 121
  • A good catch here is the "if other_dict is None", but why do you not use: "if not other_dict"? – Patrick Jul 28 '17 at 14:45
  • @user1934514 To be honest I'm making the assumption that `other_dict` will never contain `None`, which I'm not sure if is good for your case. However, I guess `0`, `False`, `""` or `[]` are too valid values, so I avoid discarding them `is None` instead of just checking its "falsy" value (the `None` values are expected to come from `other_dict.get(k)` in the previous call in the stack when `k` is not there). – jdehesa Jul 28 '17 at 14:58
  • 1
    One way to resolve that would be to use a "personal marker" in order to detect missing keys. ```marker = object()```, then the if would be ```if other_dict is not marker: ``` and you have to pass the marker as default value to the get method ```merge_dicts(v, other_dict.get(k, marker))```. – Lohmar ASHAR Sep 19 '19 at 10:04