Method chaining
Peter Otten
__peter__ at web.de
Fri Nov 22 07:08:03 EST 2013
Steven D'Aprano wrote:
> A frequently missed feature is the ability to chain method calls:
>
> x = []
> x.append(1).append(2).append(3).reverse().append(4)
> => x now equals [3, 2, 1, 4]
>
>
> This doesn't work with lists, as the methods return None rather than
> self. The class needs to be designed with method chaining in mind before
> it will work, and most Python classes follow the lead of built-ins like
> list and have mutator methods return None rather than self.
>
> Here's a proof-of-concept recipe to adapt any object so that it can be
> used for chaining method calls:
>
>
> class chained:
> def __init__(self, obj):
> self.obj = obj
> def __repr__(self):
> return repr(self.obj)
> def __getattr__(self, name):
> obj = getattr(self.obj, name)
> if callable(obj):
> def selfie(*args, **kw):
> # Call the method just for side-effects, return self.
> _ = obj(*args, **kw)
> return self
> return selfie
> else:
> return obj
>
>
> chained([]).append(1).append(2).append(3).reverse().append(4)
> => returns [3, 2, 1, 4]
>
>
> Tested, and works, in CPython 2.4 through 2.7, 3.2 and 3.3, Jython 2.5,
> and IronPython 2.6.
>
> See here for further discussion of the limitations:
>
> http://code.activestate.com/recipes/578770-method-chaining/
Here's my take:
class Chained(object):
def __init__(self, value, method=None):
self.value = value
self.method = method
def __call__(self, *args, **kw):
result = self.method(*args, **kw)
if result is None:
result = self.value
return Chained(result)
def __getattr__(self, name):
return Chained(self.value, getattr(self.value, name))
if __name__ == "__main__":
print(Chained([]).append(1).append(2).append(3).reverse().append(4).value)
print(Chained([]).append(1).extend([2,1,1]).count(1).value)
These things are nice to write as long as you omit the gory details, but
personally I don't want to see the style it favours in my or other people's
code.
More information about the Python-list
mailing list