4
\$\begingroup\$

I am getting familiar with decorators, and after a couple of tutorials i came up with a decorator that might is useful for me. Is this the proper way to use decorators? Any suggestion about the code?

def timeit_decorator(original_function):
    import time
    import colorama

    def wrapper(*args, **kwargs):
        tm = time.time()
        res = original_function(*args, **kwargs)
        print("Function " + colorama.Fore.GREEN + original_function.__name__ + colorama.Style.RESET_ALL + " executed in " + colorama.Fore.GREEN + "~{:.2f}".format(
            (time.time() - tm) / 60) + colorama.Style.RESET_ALL + " minutes!")
        return res

    return wrapper

Example of usage:

@timeit_decorator
def display(msg):
    time.sleep(10)
    return msg


print(display(1))
\$\endgroup\$
7
\$\begingroup\$

When using decorators, you should familiarize yourself with functools.wraps. Consider the difference in available informations between these two (useless) decorators:

from functools import wraps


def decorate1(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper


def decorate2(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

When applied to a function:

@decorate1
def test1(x, y, z):
    "test1 docstring"
    print(x, y, z)


@decorate2
def test2(x, y, z):
    "test2 docstring"
    print(x, y, z)

The help on each function look quite different:

>>> help(test1)
Help on function wrapper in module __main__:

wrapper(*args, **kwargs)

>>> help(test2)
Help on function test2 in module __main__:

test2(x, y, z)
    test2 docstring

Since you’re using Python 3, you should consider using better timing primitives. Namely time.perf_counter():

def wrapper(*args, **kwargs):
    begin = time.perf_counter()
    res = original_function(*args, **kwargs)
    elapsed = time.perf_counter() - begin

Lastly, you should consider using format all along instead of string concatenation:

import time
import colorama
from functools import wraps


def timeit_decorator(original_function):
    @wraps(original_function)
    def wrapper(*args, **kwargs):
        begin = time.perf_counter()
        result = original_function(*args, **kwargs)
        elapsed = time.perf_counter() - begin
        print("Function {color}{}{reset} executed in {color}"
              "~{:.2f}{reset} minutes!"
              .format(
                  original_function.__name__,
                  elapsed / 60,
                  color=colorama.Fore.GREEN,
                  reset=colorama.Style.RESET_ALL))
        return result
    return wrapper
\$\endgroup\$
  • \$\begingroup\$ Thanks a lot for the long answer, it was really helpful! \$\endgroup\$ – Gábor Erdős Dec 15 '16 at 13:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.