Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 287
wim Avatar asked Jun 02 '16 06:06

wim


People also ask

Can I use two dictionaries to merge?

You can merge two dictionaries using the | operator. It is a very convenient method to merge dictionaries; however, it is only used in the python 3.9 version or more.

How do I merge one dictionary with another?

Dictionaries can also be merged by using the unpacking operator (**). It is a unary operator with a dict object as an operand. It adds each k-v pair in an empty dictionary. Obviously, if the second dictionary is also unpacked, the value of the existing key will be updated.

What is the difference between list comprehension and dict comprehension?

Its syntax is the same as List Comprehension. It returns a generator object. A dict comprehension, in contrast, to list and set comprehensions, needs two expressions separated with a colon. The expression can also be tuple in List comprehension and Set comprehension.

Does append work on dictionaries?

To add an item to a Python dictionary, you should assign a value to a new index key in your dictionary. Unlike lists and tuples, there is no add() , insert() , or append() method that you can use to add items to your data structure.


2 Answers

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'} 
like image 193
machine yearning Avatar answered Oct 04 '22 16:10

machine yearning


To me, the obvious way is:

d_out = {} for d in ds:     d_out.update(d) 

This is quick and probably quite performant. I don't know that I can speak for the python developers, but I don't know that your expected version is more easy to read. For example, your comprehension looks more like a set-comprehension to me due to the lack of a :. FWIW, I don't think there is any technical reason (e.g. parser ambiguity) that they couldn't add that form of comprehension unpacking.

Apparently, these forms were proposed, but didn't have universal enough support to warrant implementing them (yet).

like image 21
mgilson Avatar answered Oct 04 '22 18:10

mgilson