Search code examples
pythondictionarysyntax-errorpython-3.5dict-comprehension

Dict merge in a dict comprehension


In python 3.5, we can merge dicts by using double-splat unpacking

>>> d1 = {1: 'one', 2: 'two'}
>>> d2 = {3: 'three'}
>>> {**d1, **d2}
{1: 'one', 2: 'two', 3: 'three'}

Cool. It doesn't seem to generalise to dynamic use cases, though:

>>> ds = [d1, d2]
>>> {**d for d in ds}
SyntaxError: dict unpacking cannot be used in dict comprehension

Instead we have to do reduce(lambda x,y: {**x, **y}, ds, {}), which seems a lot uglier. Why the "one obvious way to do it" is not allowed by the parser, when there doesn't seem to be any ambiguity in that expression?


Solution

  • It's not exactly an answer to your question but I'd consider using ChainMap to be an idiomatic and elegant way to do what you propose (merging dictionaries in-line):

    >>> from collections import ChainMap
    >>> d1 = {1: 'one', 2: 'two'}
    >>> d2 = {3: 'three'}
    >>> ds = [d1, d2]
    >>> dict(ChainMap(*ds))
    {1: 'one', 2: 'two', 3: 'three'}
    

    Although it's not a particularly transparent solution, since many programmers might not know exactly how a ChainMap works. Note that (as @AnttiHaapala points out) "first found is used" so, depending on your intentions you might need to make a call to reversed before passing your dicts into ChainMap.

    >>> d2 = {3: 'three', 2: 'LOL'}
    >>> ds = [d1, d2]
    >>> dict(ChainMap(*ds))
    {1: 'one', 2: 'two', 3: 'three'}
    
    >>> dict(ChainMap(*reversed(ds)))
    {1: 'one', 2: 'LOL', 3: 'three'}