[Python-ideas] Method chaining notation
Ron Adam
ron3200 at gmail.com
Mon Feb 24 17:08:18 CET 2014
On 02/24/2014 09:12 AM, Paul Moore wrote:
> On 24 February 2014 15:01, Ron Adam<ron3200 at 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
More information about the Python-ideas
mailing list