Function chaining in Python
Solution 1:
I don't know whether this is function chaining as much as it's callable chaining, but, since functions are callables I guess there's no harm done. Either way, there's two ways I can think of doing this:
Sub-classing int
and defining __call__
:
The first way would be with a custom int
subclass that defines __call__
which returns a new instance of itself with the updated value:
class CustomInt(int):
def __call__(self, v):
return CustomInt(self + v)
Function add
can now be defined to return a CustomInt
instance, which, as a callable that returns an updated value of itself, can be called in succession:
>>> def add(v):
... return CustomInt(v)
>>> add(1)
1
>>> add(1)(2)
3
>>> add(1)(2)(3)(44) # and so on..
50
In addition, as an int
subclass, the returned value retains the __repr__
and __str__
behavior of int
s. For more complex operations though, you should define other dunders appropriately.
As @Caridorc noted in a comment, add
could also be simply written as:
add = CustomInt
Renaming the class to add
instead of CustomInt
also works similarly.
Define a closure, requires extra call to yield value:
The only other way I can think of involves a nested function that requires an extra empty argument call in order to return the result. I'm not using nonlocal
and opt for attaching attributes to the function objects to make it portable between Pythons:
def add(v):
def _inner_adder(val=None):
"""
if val is None we return _inner_adder.v
else we increment and return ourselves
"""
if val is None:
return _inner_adder.v
_inner_adder.v += val
return _inner_adder
_inner_adder.v = v # save value
return _inner_adder
This continuously returns itself (_inner_adder
) which, if a val
is supplied, increments it (_inner_adder += val
) and if not, returns the value as it is. Like I mentioned, it requires an extra ()
call in order to return the incremented value:
>>> add(1)(2)()
3
>>> add(1)(2)(3)() # and so on..
6
Solution 2:
You can hate me, but here is a one-liner :)
add = lambda v: type("", (int,), {"__call__": lambda self, v: self.__class__(self + v)})(v)
Edit: Ok, how this works? The code is identical to answer of @Jim, but everything happens on a single line.
-
type
can be used to construct new types:type(name, bases, dict) -> a new type
. Forname
we provide empty string, as name is not really needed in this case. Forbases
(tuple) we provide an(int,)
, which is identical to inheritingint
.dict
are the class attributes, where we attach the__call__
lambda. -
self.__class__(self + v)
is identical toreturn CustomInt(self + v)
- The new type is constructed and returned within the outer lambda.
Solution 3:
If you want to define a function to be called multiple times, first you need to return a callable object each time (for example a function) otherwise you have to create your own object by defining a __call__
attribute, in order for it to be callable.
The next point is that you need to preserve all the arguments, which in this case means you might want to use Coroutines or a recursive function. But note that Coroutines are much more optimized/flexible than recursive functions, specially for such tasks.
Here is a sample function using Coroutines, that preserves the latest state of itself. Note that it can't be called multiple times since the return value is an integer
which is not callable, but you might think about turning this into your expected object ;-).
def add():
current = yield
while True:
value = yield current
current = value + current
it = add()
next(it)
print(it.send(10))
print(it.send(2))
print(it.send(4))
10
12
16