4 ms·
I love the union syntax in 3.9+: new_dict = old_dict | update_keys_and_values_dict
by peter422 1y ago
I love the union syntax in 3.9+:
new_dict = old_dict | update_keys_and_values_dict
- parpfish 1y agoDon’t forget the in place variant! the_dict |= update_keys_and_values_dict
- masklinn 1y agoNo no, do forget about it: like += for lists, |= mutates “the dict”, which often makes for awkward bugs. And like += over list.extend, |= over dict.update is very little gain, and restricts legal locations (augmented assignments are statements, method calls are expressions even if they return "nothing")
- IgorPartola 1y agoThe |= does exactly what it says on the tin. How could it not mutate the left side of the assignment?
- parpfish 1y agoIn typed languages, I’m all about using nice safe immutable variables/values. But in python, everything is mutable so there’s only so much safety you can wring out of adhering the an immutable style. Any other function can hop in and start mutating things (even your “private” attributes). Plan for mutations occurring everywhere.
- graemep 1y agoI find the fewer mutations the easier code is to understand, at the level of an individual function. Of course you do not have the safety you would have in a language that enforces immutability, but there is still a cost (in terms of maintenance and the likelihood of bugs) to mutation.
- masklinn 1y ago> The |= does exactly what it says on the tin. How could it not mutate the left side of the assignment? The normal way? If the LHS is an integer. |= updates the binding but does not mutate the object. Nothing requires that |= mutate the LHS let alone do so unconditionally (e.g. it could update the LHS in place as an optimisation iff the refcount indicated that was the only reference, which would optimise the case where you create a local then update it in multiple steps, but would avoid unwittingly updating a parameter in-place). edit: you might not be understanding what dict.__ior__ is doing: >>> a = b = {} >>> c = {1: 2} >>> b |= c >>> a {1: 2} That is, `a |= b` does not merely desugar to `a = a | b`, dict.__ior__ does a `self.update(other)` internally before updating the binding to its existing value. Which also leads to this fun bit of trivial (most known from list.__iadd__ but "working" just as well here): >>> t = ({},) >>> t[0] |= {1: 2} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment >>> t ({1: 2},)
- dumah 1y agoThat’s not “the normal way”, it’s just the case when the LHS is immutable. This behavior is congruent to C++ and it’s documented and idiomatic. It’d be weird if the in-place operator deeply copied the LHS, and problematic for a garbage-collected language in high throughput applications.
- masklinn 1y ago> That’s not “the normal way”, it’s just the case when the LHS is immutable. It’s the default behaviour of |= if you override |, you need to specifically override |= to do otherwise. > This behavior is congruent to C++ An excellent reason to do something else in and of itself. > it’s documented and idiomatic. Documenting a trap does not make it less of a trap. Innocently replacing | by |= induces a massive behavioural change which the user has little reason to expect, and which can easily lead to bugs due to mutating shared data. > It’d be weird if the in-place operator deeply copied the LHS I see you understand neither Python nor the behaviour of the | operator. > problematic for a garbage-collected language in high throughput applications. dict.update exists, if you specifically need to update the LHS in place it works just fine.