HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

First touch with python decorator

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withfirstpythondecoratortouch

Problem

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))

Solution

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

Code Snippets

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
@decorate1
def test1(x, y, z):
    "test1 docstring"
    print(x, y, z)


@decorate2
def test2(x, y, z):
    "test2 docstring"
    print(x, y, z)
>>> 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
def wrapper(*args, **kwargs):
    begin = time.perf_counter()
    res = original_function(*args, **kwargs)
    elapsed = time.perf_counter() - begin
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

Context

StackExchange Code Review Q#149976, answer score: 7

Revisions (0)

No revisions yet.