Merge two Python dictionaries in a single expression?

Total Post:47

 1738  View(s)
Rate this:
I have two Python dictionaries, and I want to write a single expression that returns these two dictionaries, merged. The update() method would be what I need, if it returned its result instead of modifying a dict in-place.
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = x.update(y)
>>> print z
>>> x
{'a': 1, 'b': 10, 'c': 11}
How can I get that final merged dict in z, not x?

(To be extra-clear, the last-one-wins conflict-handling of dict.update() is what I'm looking for as well.)
  1. Post:397

    Re: Merge two Python dictionaries in a single expression?

    Say you have two dicts and you want to merge them into a new dict without altering the original dicts:

    x = {'a': 1, 'b': 2}
    y = {'b': 3, 'c': 4}
    y will come second and its values will replace x's values, thus 'b' will point to 3 in our final result.

    The Pythonic way to do this is a two-step process:
    z = x.copy()

    New syntax, proposed in PEP 448, that is slated to become available in Python 3.5, is
    z = {**x, **y}

    But, although it is now showing as implemented in the release schedule for 3.5, PEP 478, it is still so new that it is not even in the What's New in Python 3.5 document. Since many organizations are still on Python 2, it is unlikely to be available in a production environment for years

    In a single expression

    The new solution coming in Python 3.5 will become the main solution to this question.

    However, if you're working in the real world and you want this in a single expression, the most performant approach is to put it in a function:
    def merge_two_dicts(x, y):
        '''Given two dicts, merge them into a new dict as a shallow copy.'''
        z = x.copy()
        return z
    and then you have a single expression:

    z = merge_two_dicts(x, y)
    You can also make a function to merge an undefined number of dicts, from zero to a very large number:

    def merge_dicts(*dict_args):
        Given any number of dicts, shallow copy and merge into a new dict,
        precedence goes to key value pairs in latter dicts.
        result = {}
        for dictionary in dict_args:
        return result
    This function will work in Python 2 and 3 for all dicts. e.g. given dicts a to g:

    z = merge_dicts(a, b, c, d, e, f, g) 
    and key value pairs in g will take precedence over dicts a to f, and so on.

    Critiques of Other Answers

    Don't use what you see in the most answers

    z = dict(x.items() + y.items())
    In Python 2, you create two lists in memory for each dict, and then creates a third list in memory with length equal to the length of the first two put together, then you discard all three lists to create the dict. In Python 3, this will fail because you're adding two dict_items objects together, not two lists -

    >>> c = dict(a.items() + b.items())
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'
    and you would have to explicitly create them as lists, e.g. z = dict(list(x.items()) + list(y.items())). This is a waste of resources and computation power.

    Similarly, taking the union of items() in Python 3 (viewitems() in Python 2.7) will also fail when unhashable objects are values (like lists, for example), and even if they are, since sets are semantically unordered, the behavior is undefined with regard to precedence. So don't do this:

    >>> c = dict(a.items() | b.items())
    Another hack you should not use:

    z = dict(x, **y)
    This uses the dict constructor, and is very fast and memory efficient (even slightly more-so than our two-step process) but unless you know precisely what is happening here (that is, the second dict is being passed as keyword arguments to the dict constructor), it's difficult to read, it's not the intended usage, and so it is not Pythonic. Also, this fails in Python 3 when keys are not strings.

    >>> c = dict(a, **b)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: keyword arguments must be strings
    If you just want a manual ad-hoc solution, keep in mind that this approach will be much less performant than copy and update because it is assigning each key-value pair, instead of all at once, but it does respect the order of precedence (latter dicts have precedence):

    {k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7
    or in python 2.6 (and perhaps as early as 2.4 when generator expressions were introduced):

    dict((k, v) for d in dicts for k, v in d.items())