On 2014-02-24, at 17:08 , Ron Adam <ron3200@gmail.com> wrote:
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.
The chained version is not intrinsically more efficient, the Python compiler could be smart enough to not LOAD_FAST (ret) repeatedly (if that proves beneficial to execution speed, which I'm not even certain of, and either way it's going to be extremely minor compared to the actual cost of executing methods). AFAIK the peephole optimiser does not even bother eliding out pairs of STORE_FAST $name LOAD_FAST $name e.g. as far as I know a = foo() a.bar() compiles to: 0 LOAD_* 0 (foo) 3 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 6 STORE_FAST 0 (a) 9 LOAD_FAST 0 (a) 12 LOAD_ATTR 1 (bar) 15 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 18 POP_TOP the pair (6, 9) is a noop and could trivially be removed (in the absence of jumps around). According to [0] a patch implementing this (although without taking care of jumps) was rejected:
because apparently the additional six lines of code didn’t buy enough of a speed improvement for an uncommon case.
(although no link to the patch so he might have been optimizing the triplet of (STORE_FAST, LOAD_FAST, RETURN_VALUE)). If removing 2 bytecode instructions once in a while does not sway the core team, I really can't see removing a single one even more rarely doing so.
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.)
Why? There's no need for LOAD_MUTATE_ATTR. And LOAD_ATTR calls __getattribute__ (and __getattr__ if necessary), a bound method is a form callable attribute, the bytecode for a method call (assuming an object on the stack) is LOAD_ATTR $attrname CALL_FUNCTION that the function mutates the original object (or not) has no relevance to attribute loading.
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
That bytecode's not correct for the case: * the return value of each method call needs to be discarded with a POP_TOP * LOAD_ATTR needs an object on the stack so you need a DUP_TOP before each LOAD_ATTR (update) (you can create the correct bytecode with something like byteplay, it'll work) [0] http://www.coactivate.org/projects/topp-engineering/blog/2008/11/03/optimizi...