[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