Python decorators are just wrappers… that return functions

Part of my issue with understanding how decorators work was that I was treating them as a function/class that is returning a variable. I should have been thinking of them as a function/class returning a modified (i.e. decorated) function/class. As mentioned succinctly here, “Decorators are a form of metaprogramming; they enhance the action of the function or method they decorate.” So a decorator is just a wrapper of the function that you want to modify (that returns a function).

So if we want to decorate the function func with the function decor then the sequence of evaluation would be modified_func = decor(func)(func_args).

Before going into what a decorator is, it’s useful to understand what it isn’t. Casually wrapping a function in another function may not be sufficient. To Illustrate this I’ve created a function to_decorate (a function that returns its input divided by 2) that I want to decorate.

def to_decorate(x):
    return(x/2)
to_decorate(2)

The not_decorator function below does not act as a decorator even though it is a wrapper for to_decorate. The not_decorator function is modifying the result of to_decorate instead of modifying the to_decorate function itself. We have to actually evaluate the function that is the argument to not_decorator. Because we have to evaluate it, the function that is the argument to not_decorator must also take in an argument. The result is that this function ends up returning a variable rather than a function/class.

# modifying the output of func so func has to be evaluated not modified
def not_decorator(func):
    return(abs(func))

The code below will error out since the to_decorate function is looking for an argument.

not_decorated = not_decorator(to_decorate)

Passing an argument will allow it to run, but it’s still not a decorator since it’s returning a variable instead of a function.

not_decorated = not_decorator(to_decorate(2))
print(not_decorated)

Let’s define the function decorator which takes a function as an input, evaluates the absolute value of the result of the function (the modification), a returns a function as opposed to the result.

We have to wrap the modification in the inner wrapper otherwise we won’t be able to return a function/class. This was the issue with not_decorator() above.

What about modifying a function with multiple decorators?

What about passing arguments to decorators?