On 02/24/2014 09:12 AM, Paul Moore wrote:
On 24 February 2014 15:01, Ron Adam<ron3200@gmail.com> wrote:
You would probably see it used more often like this...
def names(defaults, pos_names, pos_args, kwds): return {}.=update(defaults) \ .=update(zip(pos_names, pos_args) \ .=update(kwds) How is this better than
def names(defaults, pos_names, pos_args, kwds): ret = {} ret.update(defaults) ret.update(zip(pos_names, pos_args) ret.update(kwds) return ret
(I originally named the return value _ to cater for the tendency to insist on punctuation rather than names in this thread, but honestly, why*not* name the thing "ret"?)
I get the idea of chained updates, I really do. But translating between mutation of a named value and chained updates is pretty trivial, so I don't see how this is anything but a case of "follow the preferred style for the language/API you're using". And Python uses updating named values, why is that so bad?
It's not bad, just not as good. The chained expression is more efficient and can be used in places where you can't use more than a single expression. The point is to maintain both a visual and computational separation of mutable and immutable expressions. Compare the byte code from these. You can see how the chained version would be more efficient. Cheers, Ron def names(defaults, pos_names, pos_args, kwds): ret = {} ret.update(defaults) ret.update(zip(pos_names, pos_args)) ret.update(kwds) return ret
dis(names) 2 0 BUILD_MAP 0 3 STORE_FAST 4 (ret)
3 6 LOAD_FAST 4 (ret) 9 LOAD_ATTR 0 (update) 12 LOAD_FAST 0 (defaults) 15 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 18 POP_TOP 4 19 LOAD_FAST 4 (ret) 22 LOAD_ATTR 0 (update) 25 LOAD_GLOBAL 1 (zip) 28 LOAD_FAST 1 (pos_names) 31 LOAD_FAST 2 (pos_args) 34 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 37 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 40 POP_TOP 5 41 LOAD_FAST 4 (ret) 44 LOAD_ATTR 0 (update) 47 LOAD_FAST 3 (kwds) 50 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 53 POP_TOP 6 54 LOAD_FAST 4 (ret) 57 RETURN_VALUE By using the '.' we can see the difference. The byte code should be very close to this, even though this function will give an error if you try to run it. (Can't update None.) The actual difference would probably be replacing LOAD_ATTR with LOAD_MUTATE_ATTR, Which would call __getmutatemethod__ instead of __getmethod__. (or something similar to that, depending on how it's implemented.) def names(defaults, pos_names, pos_args, kwds): return {}.update(defaults) \ .update(zip(pos_names, pos_args)) \ .update(kwds)
dis(names) 2 0 BUILD_MAP 0 3 LOAD_ATTR 0 (update) 6 LOAD_FAST 0 (defaults) 9 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 12 LOAD_ATTR 0 (update)
3 15 LOAD_GLOBAL 1 (zip) 18 LOAD_FAST 1 (pos_names) 21 LOAD_FAST 2 (pos_args) 24 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 27 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 30 LOAD_ATTR 0 (update) 4 33 LOAD_FAST 3 (kwds) 36 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 39 RETURN_VALUE