# Function composition in Python3

I wanted to implement a version of rambda's compose function in python. I also implemented some examples to show its working. However, I would like the community's input on my code:

These are the methods I use for testing

from functools import reduce, partial
import random
import math

def calcMean(iterable):
return sum(iterable) / len(iterable)

def formatMean(mean):
return round(float(mean), 2)

return val + value

def multiplier(val, value):
return val * value

def isEven(val):
return val % 2 == 0


The actual compose function

def compose(*fargs):
def inner(arg):
if not arg:
raise ValueError("Invalid argument")
if not all([callable(f) for f in fargs]):
raise TypeError("Function is not callable")
return reduce(lambda arg, func: func(arg), fargs, arg)
return inner


The first exercise is something I came up with. In it I populate a list of length 10000 with random numbers from 0 to 10000, find the mean, round it to 2 decimal places, add 1 to it , then floor it and finally print out whether it is even or not.

if __name__ == '__main__':
# Ex1
rand_range = [random.randint(0, 10000) for x in range(0, 10000)]

isRandIntEven = compose(calcMean, formatMean,

print(isRandIntEven(rand_range))


The second exercise is taken from rambda's compose page. I tried to see if I could replicate the example (theirs is much more elegant)

    # Ex2
classyGreeting = lambda firstName, lastName: "The name's " + \
lastName + ", " + firstName + " " + lastName
yellGreeting = compose(partial(
classyGreeting, "James".upper()), str.upper.__call__)

print(yellGreeting("Bond"))


The last example is also taken from rambda's compose page

# Ex3
print(compose(
partial(multiplier, value=2),
math.fabs)(-4))


Suggestions for compose:

def compose(*fargs):
def inner(arg):
if not arg:
raise ValueError("Invalid argument")
if not all([callable(f) for f in fargs]):
raise TypeError("Function is not callable")
return reduce(lambda arg, func: func(arg), fargs, arg)
return inner

1. if not arg: What's wrong with arg being falsey? Perhaps that's an acceptable input. Input validation should be designated to the functions being composed, which can raise their own errors.
2. if not all([callable(f) ...: This line should be before def inner(args). It should be a single-time input validation to ensure that all given inputs in fargs are callable. Currently, it checks needless amounts of times (each inner call) and does not check immediately.
3. if not all([callable(f) ...: You can use a generator expression instead of a list comprehension for this, slightly improving readability.

If both of these suggestions are implemented, your compose would look like:

def compose(*fargs):
if not all(callable(f) for f in fargs):
raise TypeError("Function is not callable")
def inner(arg):
return reduce(lambda arg, func: func(arg), fargs, arg)
return inner


• Yeah I understand your point with the functions that are being composed handling the case of arg being falsy. Oh didn't know writing all() like all(callable(f) .... turns it into a generator. Thanks for your input – CasualCoder3 Dec 5 '17 at 6:24
• @CasualCoder3 It doesn't turn the result of all(... into a generator, if that's what you mean. The "actual" call is all( (callable(f) for f in fargs) ), which passes the generator produces by (callable(f) for f in fargs) into all and evaluates to a boolean. (I call it the "actual" call because it explicitly shows the generator expression by including its ()'s. Python allows for omitting of ()'s around a generator expression if it's the sole argument to a function). – Quelklef Dec 5 '17 at 13:37