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)
None
>>> 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.)
In a follow-up answer, you asked about the relative performance of these two alternatives:
On my machine, at least (a fairly ordinary x86_64 running Python 2.5.2), alternative
z2
is not only shorter and simpler but also significantly faster. You can verify this for yourself using thetimeit
module that comes with Python.Example 1: identical dictionaries mapping 20 consecutive integers to themselves:
z2
wins by a factor of 3.5 or so. Different dictionaries seem to yield quite different results, butz2
always seems to come out ahead. (If you get inconsistent results for the same test, try passing in-r
with a number larger than the default 3.)Example 2: non-overlapping dictionaries mapping 252 short strings to integers and vice versa:
z2
wins by about a factor of 10. That's a pretty big win in my book!After comparing those two, I wondered if
z1
's poor performance could be attributed to the overhead of constructing the two item lists, which in turn led me to wonder if this variation might work better:A few quick tests, e.g.
lead me to conclude that
z3
is somewhat faster thanz1
, but not nearly as fast asz2
. Definitely not worth all the extra typing.This discussion is still missing something important, which is a performance comparison of these alternatives with the "obvious" way of merging two lists: using the
update
method. To try to keep things on an equal footing with the expressions, none of which modify x or y, I'm going to make a copy of x instead of modifying it in-place, as follows:A typical result:
In other words,
z0
andz2
seem to have essentially identical performance. Do you think this might be a coincidence? I don't....In fact, I'd go so far as to claim that it's impossible for pure Python code to do any better than this. And if you can do significantly better in a C extension module, I imagine the Python folks might well be interested in incorporating your code (or a variation on your approach) into the Python core. Python uses
dict
in lots of places; optimizing its operations is a big deal.You could also write this as
as Tony does, but (not surprisingly) the difference in notation turns out not to have any measurable effect on performance. Use whichever looks right to you. Of course, he's absolutely correct to point out that the two-statement version is much easier to understand.
This can be done with a single dict comprehension:
In my view the best answer for the 'single expression' part as no extra functions are needed, and it is short.
Abuse leading to a one-expression solution for Matthew's answer:
You said you wanted one expression, so I abused
lambda
to bind a name, and tuples to override lambda's one-expression limit. Feel free to cringe.You could also do this of course if you don't care about copying it:
In python 3:
Out:
Docs: https://docs.python.org/3/library/collections.html#collections.ChainMap:
This should solve your problem.
This probably won't be a popular answer, but you almost certainly do not want to do this. If you want a copy that's a merge, then use copy (or deepcopy, depending on what you want) and then update. The two lines of code are much more readable - more Pythonic - than the single line creation with .items() + .items(). Explicit is better than implicit.
In addition, when you use .items() (pre Python 3.0), you're creating a new list that contains the items from the dict. If your dictionaries are large, then that is quite a lot of overhead (two large lists that will be thrown away as soon as the merged dict is created). update() can work more efficiently, because it can run through the second dict item-by-item.
In terms of time:
IMO the tiny slowdown between the first two is worth it for the readability. In addition, keyword arguments for dictionary creation was only added in Python 2.3, whereas copy() and update() will work in older versions.