(not to be confused with itertools.chain)
I was reading the following: http://en.wikipedia.org/wiki/Method_chaining
My question is: what is the best way to implement method chaining in python?
Here is my attempt:
class chain(): def __init__(self, my_object): self.o = my_object def __getattr__(self, attr): x = getattr(self.o, attr) if hasattr(x, '__call__'): method = x return lambda *args: self if method(*args) is None else method(*args) else: prop = x return prop list_ = chain([1, 2, 3, 0]) print list_.extend([9, 5]).sort().reverse() """ C:\Python27\python.exe C:/Users/Robert/PycharmProjects/contests/sof.py [9, 5, 3, 2, 1, 0] """
One problem is if calling method(*args)
modifies self.o
but doesn't return None
. (then should I return self
or return what method(*args)
returns).
Does anyone have better ways of implementing chaining? There are probably many ways to do it.
Should I just assume a method always returns None
so I may always return self.o
?
Method chaining, also known as named parameter idiom, is a common syntax for invoking multiple method calls in object-oriented programming languages. Each method returns an object, allowing the calls to be chained together in a single statement without requiring variables to store the intermediate results.
Pandas chaining is an alternative to variable assignment when transforming data. Those in favor of chaining argue that the code is easier to read because it lays out the execution of the transformation like a recipe.
The __call__ method enables Python programmers to write classes where the instances behave like functions and can be called like a function. When the instance is called as a function; if this method is defined, x(arg1, arg2, ...) is a shorthand for x.
There is a very handy Pipe
library which may be the answer to your question. For example::
seq = fib() | take_while(lambda x: x < 1000000) \ | where(lambda x: x % 2) \ | select(lambda x: x * x) \ | sum()
It's possible if you use only pure functions so that methods don't modify self.data
directly, but instead return the modified version. You also have to return Chainable
instances.
Here's an example using collection pipelining with lists:
import itertools try: import builtins except ImportError: import __builtin__ as builtins class Chainable(object): def __init__(self, data, method=None): self.data = data self.method = method def __getattr__(self, name): try: method = getattr(self.data, name) except AttributeError: try: method = getattr(builtins, name) except AttributeError: method = getattr(itertools, name) return Chainable(self.data, method) def __call__(self, *args, **kwargs): try: return Chainable(list(self.method(self.data, *args, **kwargs))) except TypeError: return Chainable(list(self.method(args[0], self.data, **kwargs)))
Use it like this:
chainable_list = Chainable([3, 1, 2, 0]) (chainable_list .chain([11,8,6,7,9,4,5]) .sorted() .reversed() .ifilter(lambda x: x%2) .islice(3) .data) >> [11, 9, 7]
Note that .chain
refers to itertools.chain
and not the OP's chain
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With