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.