1

I am trying to write a decorator that takes an arbitrary number of functions as positional arguments and then composes these functions with the function that is being decorated.

Conceptually, I am trying to do this:

@format(f1, f2, f3)
def f0(param):
    return value

f0(arg) should evaluate to f3(f2(f1(f0(arg))))

Here is a simple example I have been working on to illustrate the concept.

def lc(s):
    s = s.lower()
    return s

def punc(s):
    if s[-1] != ".":
        s = s + "."
        return s
    else:
        return s

def cap(s):
    s = s[0].upper() + s[1:]
    return s

def format(*args):
    def wrapper(func, *a):
        for f in args:
            func = f(func)
        return func
    return wrapper

@format(lc, punc, cap)
def example_func(param):
    return param

What I'm expecting is this:

f0("MY TEST STRING")

My test string.

I'm getting the following error message:

AttributeError: 'function' object has no attribute 'lower'

There may be an easier way to do this, such as pass the functions directly into the original and not use a decorator, but I am looking for a solution that specifically uses a decorator in this way. Thank you.

briancaffey
  • 2,339
  • 6
  • 34
  • 62
  • See [How to multiply functions in Python?](https://stackoverflow.com/q/30195045/674039) for a more elegant way to make functions "composable" by using decorators. – wim Jan 29 '18 at 23:49

1 Answers1

2

You need to use the "decorator-with arguments" pattern, so like this:

def format(*fs):
    def deco(f):
        def wrapper(x):
            val = f(x)
            for func in fs:
                val = func(val)
            return val
        return wrapper
    return deco
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • @briancaffey and if it doesn't make sense, think of the non-sugar version of appying a decorator: `f0 = format(lc, punc, cap)(f0)` – juanpa.arrivillaga Jan 30 '18 at 00:09
  • thanks. I just found some additional helpful resources on decorators with arguments [here](http://python-3-patterns-idioms-test.readthedocs.io/en/latest/PythonDecorators.html#decorator-functions-with-decorator-arguments). This is a helpful idiom to have, thanks for pointing it out – briancaffey Jan 30 '18 at 00:12
  • @briancaffey some people use callable classes, but I prefer this pattern. – juanpa.arrivillaga Jan 30 '18 at 00:13