On Sat, Feb 22, 2014 at 12:13 PM, Steven D'Aprano <steve@pearwood.info> wrote:
On Sat, Feb 22, 2014 at 04:30:03AM +1100, Chris Angelico wrote:
So here's the proposal. Introduce a new operator to Python, just like the dot operator but behaving differently when it returns a bound method. We can possibly use ->, or maybe create a new operator that currently makes no sense, like .. or .> or something. Its semantics would be:
1) Look up the attribute following it on the object, exactly as per the current . operator 2) If the result is not a function, return it, exactly as per current. 3) If it is a function, though, return a wrapper which, when called, calls the inner function and then returns self.
When you say "a function", do you actually mean a function? What about other callables, such as methods (instance, class or static) or types? How does this interact with the descriptor protocol?
Well, I mean anything that can become a bound method. At some point, a lookup is done and it returns a function (in the normal case), but I guess presumably any callable will do at that point. I'm looking at hooking in at the point where it becomes bound. A bound method is a function wrapper that provides a 'self' argument. A chaining bound method would be a function wrapper that provides a 'self' argument, and then returns it.
Here's an alternative: add a `chained` wrapper to, oh, let's say functools (I don't think it needs to be a built-in). In my hierarchy of language preferredness, this suggests that while Python *supports* method chaining, it isn't *preferred*. (If it were preferred, it would happen by default, or there would be syntax for it.) If you want to chain methods, you can, but it's a slightly unusual thing to do, and the fact that you have to import a module and call a wrapper class should be sufficient discouragement for casual (ab)use.
Downside of that is that it's really verbose. This sort of thing is useful only if it's short. It's like doing a filtered iteration: for spam in can if tasty: eat(spam) for spam in filter(lambda x: tasty, can): eat(spam) Using filter() is so verbose that there has been and will continue to be a stream of requests for a "filtered for loop" syntax. Having to pass everything through a named wrapper is too wordy to be useful. ChrisA