[Python-ideas] Method chaining notation

Andrew Barnert abarnert at yahoo.com
Tue Feb 25 00:29:27 CET 2014


On Feb 24, 2014, at 7:01, Ron Adam <ron3200 at gmail.com> wrote:

> On 02/24/2014 01:34 AM, Andrew Barnert wrote:
>> 
>> So, is your argument is "my code looks kind of like some horribly unreadable and unpythonic, but legal, code, and therefore it should also be legal despite being unreadable and unpythonic?"
> 
> Wow, tough crowd here.. :-)
> 
> Both the separation of the '.', and the use of the already special __add__ isn't important as far as the actual suggestion is concerned.  Those are unrelated style issues.
> 
> 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)
> 
> 
> Normally .update returns None.  The reason for that is so that it's clear you are mutating an object instead of creating a new one.

And so you can't chain it, so you can only mutate one thing in a statement. People keep assuming that's an accidental unwanted side effect of the rule, but Guido explicitly saying that he doesn't like method chaining, can't you imagine it's at least possible that this is intentional, not a bug?

> By using .=, it can return self, but still maintain the clarity between mutation and non-mutation.
> 
> This particular syntax is consistent with the use of OP+equal to mean mutate in place.  But you might prefer, "..", or something else.

You're missing a key distinction here. OP+equal does not return self. In fact, it doesn't return _anything_, because it's not an expression at all, it's a statement.

It would be very easy to make augmented assignment an expression, and even easier to make it return self (after all, it's implemented by calling dunder methods that _do_ return self!). But it wasn't designed that way. Intentionally.

> The other alternative is to use a function.  But it would be difficult to get the same behaviour along with the same efficiency.

How do you think a regular function would have less efficiency than an operator? Operators work by doing a complex chain of lookups to find a function to call. Ordinary calls do a simpler lookup. They both call the function the same way once they find it.

You're also missing the other alternative: write it Pythonically. For example:

   return merge_dicts(
       defaults,
       zip(pos_names, pos_args),
       kwds)

It's shorter, it has less extraneous syntax, it doesn't need awkward backslash continuations, and it created or modifies one value in one place. It's easier to read, and easier to reason about. Why would you want to write it the other way?

Of course that merge_dicts function isn't in the stdlib. Maybe it should be. But you can write it yourself trivially. For example:

   def merge_dicts(*args):
       return {k: v for arg in args for (k, v) in dict(arg).items()}

That nested comprehension might be a little too complicated; if you think so, you can split it into two expressions, or even write an explicit loop around dict.update. Whatever; this is something you write once and use every time you want to merge a bunch of dicts.


More information about the Python-ideas mailing list