Why doesn't python dict.update () return an object?

I am trying to do:

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 if the function was really cumbersome, and I would rather do:

  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 return an object so you can chain?

JQuery does it for the chain. Why is this not acceptable for python?

+114
python dictionary language-design language-features monads
Sep 21 '09 at 5:20
source share
11 answers

Python basically implements a pragmatically colored flavor separating request commands : mutators return None (with pragmatically-induced exceptions such as pop ; -), so they should not be confused with accessories (and in the same way, assignment is not an expression, there is a separation of the expression of the expression and etc.).

This does not mean that there are not many ways to merge things when you really want it, for example, dict(a, **award_dict) makes the new dict the same as the one you think is desirable .update returned - so why not use THIS if you really think this is important?

Edit : btw, it is not necessary, in your specific case, to create a in this way:

 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 conflict, the fact that the entries in award_dict override the ones you give explicitly to get different semantics, i.e. for have explicit entries that “win” such conflicts, pass award_dict as the only positional arg, before the keyword, and are devoid of the form ** - dict(award_dict, name=name , etc. etc.).

+187
Sep 21 '09 at 5:31
source share

The Python API, by agreement, distinguishes between procedures and functions. Functions calculate new values ​​from their parameters (including any target object); procedures change objects and return nothing (i.e. they return None). Thus, procedures have side effects, functions do not. update is a procedure, so it does not return a value.

The motivation for this is that otherwise you may get unwanted side effects. Consider

 bar = foo.reverse() 

If the reverse (which changes the list in place) also returns the list, users might think that the reverse returns a new list, which is assigned to the bar, and never notices that foo is also changing. Having made a None return return, they immediately recognize that the bar is not the result of a reversal, and will look closer, what is the effect of the reverse.

+33
Sep 21 '09 at 5:29
source share
 >>> 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 by returning the combined dict, it changes the first parameter in place. So dict_merge (a, b) will change 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} 
+13
May 24 '12 at 4:50 a.m.
source share

not enough reputation for comment left to top

@beardc is not like CPython thing. PyPy gives me "TypeError: keywords must be strings"

The solution with **kwargs only works because the dictionary that needs to be combined has type string keys .

i.e.

 >>> dict({1:2}, **{3:4}) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: keyword arguments must be strings 

against

 >>> dict({1:2}, **{'3':4}) {1: 2, '3': 4} 
+8
Jan 14 '16 at 5:14
source share

Not that this was unacceptable, but rather that dicts were not implemented that way.

If you look at Django ORM, it makes extensive use of chaining. It is not discouraging, you can even inherit from dict and only override update to update and return self if you really want to.

 class myDict(dict): def update(self, *args): dict.update(self, *args) return self 
+5
Sep 21 '09 at 5:24
source share

It is easy as:

 (lambda d: d.update(dict2) or d)(d1) 
+4
Nov 14 '18 at 15:27
source share

as close to the 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 
+2
Nov 01 '16 at 10:18
source share
 import itertools dict_merge = lambda *args: dict(itertools.chain(*[d.iteritems() for d in args])) 
0
Apr 17 '13 at 21:07
source share

I just tried it myself in Python 3.4 (so I could not use the fancy {**dict_1, **dict_2} ).

I wanted to be able to have non-dictionary keys in dictionaries, as well as provide an arbitrary number of dictionaries.

Also, I wanted to create a new dictionary, so I decided not to use collections.ChainMap (like I didn't want to use dict.update initially.

Here is what I wrote:

 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} 
0
Jun 23 '17 at 17:37 on
source share

For those who are late for the party, I gathered some time (Py 3.7), showing that the .update() -based .update() look a little (~ 5%) faster when the input is saved, and noticeably (~ 30%) faster when just updating in place.

As usual, all criteria should be taken with salt crumbs.

 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 time intervals for on-site operations are slightly more complicated, so they will need to be modified along with an additional copy operation (the first timing is for reference only):

 %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) 
0
Jun 26 '19 at 9:05
source share

For those who are late for the party, I gathered time (Py 3.7):

 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) # 306 ms ± 2.34 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) %timeit join(d1, d2) # 316 ms ± 14.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) %timeit dict(d1, **d2) # 423 ms ± 2.55 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) %timeit {**d1, **d2} # 427 ms ± 3.08 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 

join2(inplace=True) and update_or() slightly different animals

0
Jun 26 '19 at 9:15
source share



All Articles