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)
def adder(val, value):
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,
partial(adder, value=1), math.floor.__call__, isEven)
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),
partial(adder, value=1),
math.fabs)(-4))
1 Answer 1
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
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.if not all([callable(f) ...
: This line should be beforedef inner(args)
. It should be a single-time input validation to ensure that all given inputs infargs
are callable. Currently, it checks needless amounts of times (eachinner
call) and does not check immediately.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
Please tell me if I have misunderstood your code.
-
1\$\begingroup\$ 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()
likeall(callable(f) ....
turns it into a generator. Thanks for your input \$\endgroup\$CasualCoder3– CasualCoder32017年12月05日 06:24:09 +00:00Commented Dec 5, 2017 at 6:24 -
1\$\begingroup\$ @CasualCoder3 It doesn't turn the result of
all(...
into a generator, if that's what you mean. The "actual" call isall( (callable(f) for f in fargs) )
, which passes the generator produces by(callable(f) for f in fargs)
intoall
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). \$\endgroup\$Quelklef– Quelklef2017年12月05日 13:37:29 +00:00Commented Dec 5, 2017 at 13:37
Explore related questions
See similar questions with these tags.