Function decorators in python

26 Sep 2022

Function decorators are an elegant way of wrapping a function inside another function. I’m posting this here so I can always look it up but there’s good stuff on youTube too.

Suppose you want to do something every time a function is called, log it’s arguments and return value perhaps, or test how fast it runs, or cache it’s results. Decorators are your friends here.

# wrapper function
def log(fn):
    def wrapper(*args, **kwargs):
        print(fn)
        out = fn(*args, **kwargs)
        print("\tin: {}\n\tout: {}\n".format((*args,) + (kwargs,), out))
        return out
    return wrapper

# a different wrapper function
def timeThis(fn):
    def wrapper(*args, **kwargs):
        start = timer()
        out = fn(*args, **kwargs)
        end = timer()
        print("execution took: {:0.8f} seconds".format(end - start))
        return out
    return wrapper

# 'decorate' the function with the name of the decorator
@log
def add(x, y=1):
    return x+y

@timeThis
def sumOfSquaresUpTo(x):
    return sum([x**2 for x in range(1,x+1)])

add(5, y=4)
sumOfSquaresUpTo(200)

The key here is that the function you call, add for example, is actually fed into log first, log returns the wrapper function that calls add when it is itself called.