Python Decorators For Fun

Introduction

In this post we will be taking a look at python decorators. We will be building a decorator to track runtime of a function. Our decorator will handle varying arguments including keyword arguments.

TLDR

A Python decorator is a function that can accept another function as well as arguments and wraps the function in added behavior. Decorators are later used with other python functions with the following syntax.

@decorator_func_name
def my_func_to_wrap():
    pass

Time Tracking

import time

def timeit(func):
    def _decorator(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"Elapsed Time: {end - start}")
        return result
    return _decorator

First we need to import the time package. We will be using the time function which returns time in seconds since epoch.

import time

Now we define a function (our decorator) called timeit that takes a python callable as a single argument.

Within our decorator I define an inner function that will encapsulate the behavior of our decorator. The inner function can accept varying arguments *args as well as keyword arguments **kwargs. These arguments will be passed along to the wrapped function allowing for no arguments to required arguments or a mix between required and keyword arguments.

def timeit(func):
    def _decorator(*args, **kwargs):

The code that handles the tracking elapsed time is straight forward, but lets walk through it. First I take the current seconds since epoch and then call the python callable and take another sample of time since epoch, and finally print the elapsed time.

start = time.time()
result = func()
end = time.time()
print(f"Elapsed Time: {end - start}")

Last part of the decorator behavior is to return any values that were returned from the python callable.

return result

Finally, we return our newly decorated function.

return _decorator

Test Drive Time

Now that we have our newly created decorator function lets take it out for a spin. In order to reference our decorator function as a decorator we use the character @ and append the function name @timeit.

@timeit
def long_operation():
    time.sleep(3)

We define a simple function called long_operation and use our newly created decorator to wrap. Lets see what happens when we run this function in the Python REPL.

>>> long_operation()
Elapsed Time: 3.003438711166382

Perfect the function ran and we got the print message we wanted telling us how long it took for this function to run.

OK, now lets modify our function to return a string and demonstrate that this behavior is not lost from our decorator. As well as accept and use arguments.

@timeit
def long_operation(name, age):
    time.sleep(3)
    return f"Hello, {name}! You are {age} years old!"
>>> long_operation("Andrew", age=35)
EElapsed Time: 3.003110408782959
'Hello, Andrew! You are 35 years old!'

Well that’s it we created ourselves a simple decorator that can track the execution time of a function. Please register on our blog for more content!

Challenge

Modify the timeit decorator to log the start timestamp of the function run and the end timestamp of the function run.

Leave a Reply

Your email address will not be published. Required fields are marked *