Python decorators

Decorators are used to add extra functionalies to the existing python code. Basically decorators take in a function and perform some operation over it.

Let's build a decorator to time a function. This is really helpful if you want to time multiple functions to test them. The basic logic for timing is

1. start = time()
2. func()
3. end = time()
4. time = end - start

The basic structure for a decorator is,

def outer(func):
    def inner(*args, **kwargs):

        # some operation(optional)

        result = func(*args, **kwargs)

        # some operation(optional)

        return result

    return inner

# and later you can decorate any function
@outer
def new_function():
    pass

In our case, we'll get the start and end timings.

from timeit import default_timer as t

def timer(func):
    @wraps(func)
    def inner(*args, **kwargs):
        s = t()
        result = func(*args, **kwargs)
        e = t()
        print(f"{func.__name__} executed in {e-s:.4f}s")
        return result

    return inner
# now let's decorate some function
@timer
def some_function(limit: int):
    for _ in range(limit):
        sum([i ** 2 for i in range(1_000_000)])
>>> from test import some_function as sf
>>> sf(5)
some_function executed in 1.2004s
>>> sf
<function timer.<locals>.inner at 0x7fac1852fc10>

Everything looks good, except for the last line. some_function has the signature of the decoratorūüė•

decorator to the rescue

from functools import wraps. wraps will copy over the attributes to the function passed. Our decorator becomes,

def timer(func):
    @wraps(func)
    def inner(*args, **kwargs):
        s = t()
        result = func(*args, **kwargs)
        e = t()
        print(f"{func.__name__} executed in {e-s:.4f}s")
        return result

    return inner

And the final result,

>>> from test import some_function as sf
>>> sf(5)
some_function executed in 1.3348s
>>> sf
<function some_function at 0x7f522a0fcb80>

Happy hacking‚ú®‚ú®

No Comments Yet