Is there a chain calling method in Python?
Question:
Is there a function in Python that would do this:
val = f3(f2(f1(arg)))
by typing this (for example):
val = chainCalling(arg,f3,f2,f1)
I just figured, since python is (arguably) a functional language the function I’m looking for will make syntax brighter
Answers:
Use the reduce()
function to chain calls:
from functools import reduce
val = reduce(lambda r, f: f(r), (f1, f2, f3), arg)
I used the forward-compatible functools.reduce()
function; in Python 3 reduce()
is no longer in the built-in namespace.
This can also be made a separate function, of course:
from functools import reduce
def chain(*funcs):
def chained_call(arg):
return reduce(lambda r, f: f(r), funcs, arg)
return chained_call
You can use the reduce()
functool — as Martijn briantly suggested, or you can write it yourself quite simply:
def chainCalling(arg, *funcs):
if len(funcs) > 0:
return chainCalling(funcs[0](arg), funcs[1:])
return arg
or, as an alternative not using recursion — so not bound to the call stack limitation, as suggested by Martijn:
def chainCalling(arg, *funcs):
result = arg
for f in funcs:
result = f(result)
return result
Obviously, you’ll want to call it that way, to avoid an useless reversal of the arguments:
chainCalling(arg, f1, f2, f3)
It might be a tad late, but try my solution below. chain_func
essentially creates a new wrapper function that calls all underlying functions in chain when invoked in runtime.
def chain_func(*funcs):
def _chain(*args, **kwargs):
cur_args, cur_kwargs = args, kwargs
ret = None
for f in reversed(funcs):
cur_args, cur_kwargs = (f(*cur_args, **cur_kwargs), ), {}
ret = cur_args[0]
return ret
return _chain
In case you want to apply the chain of functions to multiple arguments, you can create an aggregated function.
g = lambda x: f3(f2(f1(x)))
or more flexible (when there is an arbitrary list of functions):
from functools import reduce, partial
f3 = lambda x: -x
f2 = lambda x: x ** 2
f1 = lambda x: x + 1
function_list = (f1, f2, f3)
g = partial(reduce, lambda r, f: f(r), function_list)
print(g(3)) # results in -16
I feel like these answers are correct, but do not address the spirit of the question. We can get the same behavior by creating an intermediate "chained" function definition, and we can avoid confusion about the meaning of nested labmdas by defining a function that produces the chained function.
I have used g(f(x))
here so that the functions are applied from left to right.
from functools import reduce
from typing import Iterable, Callable, Any
def chain_fns(fns: Iterable[Callable[[Any], Any]]) -> Callable[[Any], Any]:
return reduce(lambda f, g: lambda x: g(f(x)), fns)
chained_method = chain_fns([f1, f2, f3])
chained_method(arg)
Is there a function in Python that would do this:
val = f3(f2(f1(arg)))
by typing this (for example):
val = chainCalling(arg,f3,f2,f1)
I just figured, since python is (arguably) a functional language the function I’m looking for will make syntax brighter
Use the reduce()
function to chain calls:
from functools import reduce
val = reduce(lambda r, f: f(r), (f1, f2, f3), arg)
I used the forward-compatible functools.reduce()
function; in Python 3 reduce()
is no longer in the built-in namespace.
This can also be made a separate function, of course:
from functools import reduce
def chain(*funcs):
def chained_call(arg):
return reduce(lambda r, f: f(r), funcs, arg)
return chained_call
You can use the reduce()
functool — as Martijn briantly suggested, or you can write it yourself quite simply:
def chainCalling(arg, *funcs):
if len(funcs) > 0:
return chainCalling(funcs[0](arg), funcs[1:])
return arg
or, as an alternative not using recursion — so not bound to the call stack limitation, as suggested by Martijn:
def chainCalling(arg, *funcs):
result = arg
for f in funcs:
result = f(result)
return result
Obviously, you’ll want to call it that way, to avoid an useless reversal of the arguments:
chainCalling(arg, f1, f2, f3)
It might be a tad late, but try my solution below. chain_func
essentially creates a new wrapper function that calls all underlying functions in chain when invoked in runtime.
def chain_func(*funcs):
def _chain(*args, **kwargs):
cur_args, cur_kwargs = args, kwargs
ret = None
for f in reversed(funcs):
cur_args, cur_kwargs = (f(*cur_args, **cur_kwargs), ), {}
ret = cur_args[0]
return ret
return _chain
In case you want to apply the chain of functions to multiple arguments, you can create an aggregated function.
g = lambda x: f3(f2(f1(x)))
or more flexible (when there is an arbitrary list of functions):
from functools import reduce, partial
f3 = lambda x: -x
f2 = lambda x: x ** 2
f1 = lambda x: x + 1
function_list = (f1, f2, f3)
g = partial(reduce, lambda r, f: f(r), function_list)
print(g(3)) # results in -16
I feel like these answers are correct, but do not address the spirit of the question. We can get the same behavior by creating an intermediate "chained" function definition, and we can avoid confusion about the meaning of nested labmdas by defining a function that produces the chained function.
I have used g(f(x))
here so that the functions are applied from left to right.
from functools import reduce
from typing import Iterable, Callable, Any
def chain_fns(fns: Iterable[Callable[[Any], Any]]) -> Callable[[Any], Any]:
return reduce(lambda f, g: lambda x: g(f(x)), fns)
chained_method = chain_fns([f1, f2, f3])
chained_method(arg)