I have this code:
award_dict = {
"url": "http://facebook.com",
"imageurl": "http://farm4.static.flickr.com/3431/3939267074_feb9eb19b1_o.png",
"count": 1,
}
def award(name, count, points, desc_string, my_size, parent):
if my_size > count:
a = {
"name": name,
"description": desc_string % count,
"points": points,
"parent_award": parent,
}
a.update(award_dict)
return self.add_award(a, siteAlias, alias).award
But the code felt rather cumbersome. I would have preferred to be able to write:
def award(name, count, points, desc_string, my_size, parent):
if my_size > count:
return self.add_award({
"name": name,
"description": desc_string % count,
"points": points,
"parent_award": parent,
}.update(award_dict), siteAlias, alias).award
Why doesn't the update method return the original dictionary, so as to allow chaining, like how it works in JQuery? Why isn't it acceptable in python?
See How do I merge two dictionaries in a single expression in Python? for workarounds.
12 Answers 12
Python's mostly implementing a pragmatically tinged flavor of command-query separation: mutators return None (with pragmatically induced exceptions such as pop;-) so they can't possibly be confused with accessors (and in the same vein, assignment is not an expression, the statement-expression separation is there, and so forth).
That doesn't mean there aren't a lot of ways to merge things up when you really want, e.g., dict(a, **award_dict) makes a new dict much like the one you appear to wish .update returned -- so why not use THAT if you really feel it's important?
Edit: btw, no need, in your specific case, to create a along the way, either:
dict(name=name, description=desc % count, points=points, parent_award=parent,
**award_dict)
creates a single dict with exactly the same semantics as your a.update(award_dict) (including, in case of conflicts, the fact that entries in award_dict override those you're giving explicitly; to get the other semantics, i.e., to have explicit entries "winning" such conflicts, pass award_dict as the sole positional arg, before the keyword ones, and bereft of the ** form -- dict(award_dict, name=name etc etc).
17 Comments
a altogether, btw,TypeErrordict(old_dict, old_key=new_value) won't throw multiple values for keyword and return new dict.Python's API, by convention, distinguishes between procedures and functions. Functions compute new values out of their parameters (including any target object); procedures modify objects and don't return anything (i.e. they return None). So procedures have side effects, functions don't. update is a procedure, hence it doesn't return a value.
The motivation for doing it that way is that otherwise, you may get undesirable side effects. Consider
bar = foo.reverse()
If reverse (which reverses the list in-place) would also return the list, users may think that reverse returns a new list which gets assigned to bar, and never notice that foo also gets modified. By making reverse return None, they immediately recognize that bar is not the result of the reversal, and will look more close what the effect of reverse is.
8 Comments
reverse(foo) feels weird.bar=foo[:]), then revert the copy.bar = foo.reverse(), you could think that foo is not modified. To avoid confusion, you have both foo.reverse() and bar = reversed(foo)..updated and .reversed for this purpose.This is easy as:
(lambda d: d.update(dict2) or d)(d1)
Or, if it is important not to modify the dictionary:
(lambda d: d.update(dict2) or d)(d1.copy())
3 Comments
return d1.update(d2). Let's consider that d2 comes from something like self.one_function(). What is the theory behind this lambda method? Thanks for the answer.or statement; first statement in or returns None, second one - updated dictnot enough reputation for comment left on top answer
@beardc this doesn't seem to be CPython thing. PyPy gives me "TypeError: keywords must be strings"
The solution with **kwargs only works because the dictionary to be merged only has keys of type string.
i.e.
>>> dict({1:2}, **{3:4})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings
vs
>>> dict({1:2}, **{'3':4})
{1: 2, '3': 4}
Edit: Indeed this is a clever solution, but not a good solution! It is not, as pointed out by others, because one won't be able to merge a dictionary with int keys using the ** unpacking operator, see use dict as kwargs.
Here, the ** unpacking operator for the {3: 4} dictionary is used to provide named arguments (and fails because keyword arguments must be strings). To clarify, this answer was meant as a comment and an illustration that the error isn't specific to CPython, but not as solution. As a solution consider whether you need to keep both values when merging a key present in both dictionaries or if the python 3.9 | operator is an option and find a different answer.
3 Comments
>>> dict_merge = lambda a,b: a.update(b) or a
>>> dict_merge({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}
Note that as well as returning the merged dict, it modifies the first parameter in-place. So dict_merge(a,b) will modify a.
Or, of course, you can do it all inline:
>>> (lambda a,b: a.update(b) or a)({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}
4 Comments
lambda should not be used like that, instead use conventional function def insteada.update(b) or adict_merge = lambda a,b: a.update(b) or a abuses the fact that dict.update returns None and uses or to return the value of the dictionary, as None or something will always return something, this is bad. If for some reason 1 single newline is a huge waste and hassle, then even def dict_merge(a, b): a.update(b); return a is better than this, because at least with semicolon you are explicit about what you are doing rather than sneaking in some hacky code.Nowadays, with Python 3.9, you can use the | operator, see:
https://docs.python.org/3.9/whatsnew/3.9.html#dictionary-merge-update-operators
Quoting from there:
>>> x = {"key1": "value1 from x", "key2": "value2 from x"}
>>> y = {"key2": "value2 from y", "key3": "value3 from y"}
>>> x | y
{'key1': 'value1 from x', 'key2': 'value2 from y', 'key3': 'value3 from y'}
>>> y | x
{'key2': 'value2 from x', 'key3': 'value3 from y', 'key1': 'value1 from x'}
1 Comment
Its not that it isn't acceptable, but rather that dicts weren't implemented that way.
If you look at Django's ORM, it makes extensive use of chaining. Its not discouraged, you could even inherit from dict and only override update to do update and return self, if you really want it.
class myDict(dict):
def update(self, *args):
dict.update(self, *args)
return self
2 Comments
For those coming late to the party, I had put some timing together (Py 3.7), showing that .update() based methods look a bit (~5%) faster when inputs are preserved and noticeably (~30%) faster when just updating in-place.
As usual, all the benchmarks should be taken with a grain of salt.
def join2(dict1, dict2, inplace=False):
result = dict1 if inplace else dict1.copy()
result.update(dict2)
return result
def join(*items):
iter_items = iter(items)
result = next(iter_items).copy()
for item in iter_items:
result.update(item)
return result
def update_or(dict1, dict2):
return dict1.update(dict2) or dict1
d1 = {i: str(i) for i in range(1000000)}
d2 = {str(i): i for i in range(1000000)}
%timeit join2(d1, d2)
# 258 ms ± 1.47 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit join(d1, d2)
# 262 ms ± 2.97 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit dict(d1, **d2)
# 267 ms ± 2.74 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit {**d1, **d2}
# 267 ms ± 1.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
The timings for the in-place operations are a bit trickier, so it would need to be modified along an extra copy operation (the first timing is just for reference):
%timeit dd = d1.copy()
# 44.9 ms ± 495 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit dd = d1.copy(); join2(dd, d2)
# 296 ms ± 2.05 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit dd = d1.copy(); join2(dd, d2, True)
# 234 ms ± 1.02 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit dd = d1.copy(); update_or(dd, d2)
# 235 ms ± 1.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Comments
as close to your proposed solution as I could get
from collections import ChainMap
return self.add_award(ChainMap(award_dict, {
"name" : name,
"description" : desc_string % count,
"points" : points,
"parent_award" : parent,
}), siteAlias, alias).award
Comments
Just been trying this myself in Python 3.4 (so wasn't able to use the fancy {**dict_1, **dict_2} syntax).
I wanted to be able to have non-string keys in dictionaries as well as provide an arbitrary amount of dictionaries.
Also, I wanted to make a new dictionary so I opted to not use collections.ChainMap (kinda the reason I didn't want to use dict.update initially.
Here's what I ended up writing:
def merge_dicts(*dicts):
all_keys = set(k for d in dicts for k in d.keys())
chain_map = ChainMap(*reversed(dicts))
return {k: chain_map[k] for k in all_keys}
merge_maps({'1': 1}, {'2': 2, '3': 3}, {'1': 4, '3': 5})
# {'1': 4, '3': 5, '2': 2}
Comments
Merge by concatenating list of items or dict union:
d1 = {1: "one"}
d2 = {2: "two"}
dict(list(d1.items()) + list(d2.items()))
# {1: 'one', 2: 'two'}
d1 | d2
# {1: 'one', 2: 'two'}
Comments
Explore related questions
See similar questions with these tags.
newdict = dict(dict001, **dict002)newdict = {k: v for k, v in list(dict001.items()) + list(dict002.items())}