Python Decorator

There’s another cool feature in Python that allows us to extend the functionality of an existing function without even modifying it, and I’m talking about “decorators”.

So let’s take a super simple example code like this:


def add(n1, n2):
    return n1 + n2

num1 = 1
num2 = 4
answer = add(num1, num2)

print(f"Sum of {num1} and {num2} is {answer}")

Just your basic routine, define a function, define some values, call the function and print the result. Nothing spectacular…yet 🙂

This will just produce this lovely output (as expected):

So, let’s just say the boss says, “we need to log every time that ‘add’ function is called, but only within our app (since that function is used by many other apps)”. Typical work scenario, right? 🙂

Well, that’s where decorators come in — essentially it wraps or “decorates” an existing function so we can do something like log something on the screen when it’s being called.

Let me show you a very simple implementation of decorator for our use case:

def log_api(fnc, *args, **kwargs):
    
    def wrapper(*args, **kwargs):                               # this is a wrapper function
        print(f"about to execute {fnc.__name__} function")      # this is what we want to do
        return fnc(*args, **kwargs)                             # this calls the original function
    return wrapper

A decorator is just a normal function that accepts a function as (one of) its argument so we can defer the call to it. We then add the positional (*args) and keyword (**kwargs) arguments so we can accept functions with various signatures (or number of parameters).

So how do we use this to achieve what the boss wants?

Glad you asked 🙂

We just put that on top of the ‘add’ function with the ‘@’ prefix:

@log_api
def add(n1, n2):
    return n1 + n2

Now, run it again and you’ll see the magic!

As you can see, it printed something else before calling it 🙂

Cool, now you can see that we really didn’t do anything to the original function. So that means our extra code/behavior will only happen inside our application.

Another way to do this (if your use case prevents you from using it as decorator) is to call it like a regular function like this:

add = log_api(add)          # call our wrapper function, passing the original 'add' function to it
answer = add(num1, num2)
print(f"Sum of {num1} and {num2} is {answer}")

This will produce the same result as using it as decorator.

Using it as a decorator just looks cleaner though 🙂

Leave a Comment