On 02/24/2014 05:29 PM, Andrew Barnert wrote:
On Feb 24, 2014, at 7:01, Ron Adam<ron3200@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?
Yes, I know it's an intentional design choice. Did he ever say why he doesn't like chained methods? I tried to look it up, and all I found was a lot of other people saying he doesn't. But nothing that indicated what his reasoning for it was.
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.
I was referring to the dunder method that gets called as you noted below.
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.
How else would it be designed?
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.
Ok, you lost me with this.. what complex chain of lookups are you referring to? (Besides the normal name and method resolution.) Operators are one level above methods... And yes, so are direct method calls. But the alternative I was talking about was to write a function to get the same behaviour with chained methods as I was describing, That adds another layer, so obviously it wouldn't be quite as efficient. I wasn't saying functions aren't efficient.
You're also missing the other alternative: write it Pythonically. For example:
Never said writing functions was bad. I use functions all the time to make code nicer and cleaner. So no, I'm not missing that.
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.
Much slower too. Yes, the dict.update is nicer and quicker than the comprehension. I'd probably do it this way... def merge_dicts(*args): D = {} for a in args: D.update(a) return D Chaining methods isn't a do everything everywhere kind of thing. There are times when it's handy and times when a function is better. Cheers, Ron