6
\$\begingroup\$

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))
\$\endgroup\$

1 Answer 1

8
\$\begingroup\$

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

Please tell me if I have misunderstood your code.

\$\endgroup\$
2
  • 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() like all(callable(f) .... turns it into a generator. Thanks for your input \$\endgroup\$ 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 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). \$\endgroup\$
    – Quelklef
    Dec 5, 2017 at 13:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.