Monday, April 10, 2017

Python and Decorators

There are tons of tutorials with great step by step instructions to give you every bit of details on how to use it and all the advanced features you might want to have... this tutorial will be very high level with what I did with decorators to benefit me in a real world situation.

I was writing a flask app and I wanted to capture time that it took to run each route... the naive approach would be to try to insert the logic into every routine... better option is to use decorators!

Here is what I did.

Add util.py that would contain time logging among other things with following code

util.py code
--------------
# logging
logger = logging.getLogger("utils")
logger.setLevel(logging.INFO)
fh = logging.FileHandler('logs/retrieve_stock_info.log')
formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s",
                              "%Y-%m-%d %H:%M:%S")
fh.setFormatter(formatter)
logger.addHandler(fh)


def log_time(function_name):
    def log_it(func):
        @functools.wraps(func)
        def measure_and_log_time(*args, **kwargs):
            start_time = time.time()
            returned_view = func(*args, **kwargs)
            logger.info("--- Function %s ran in %s seconds ---" % (function_name, time.time() - start_time))
            return returned_view
        return measure_and_log_time
    return log_it
--------------


then in your route define

@main.route('/corr')
@utils.log_time("corr")
def corr():

That's all!

Here is a good template to use for decorators:

----------------
# Decorator without parameters
import functools

def my_decorator(func):
@functools.wraps(func)
def functions_that_runs_func():
print("In the decorator")
func()
print("After the decorator")
return function_that_runs_func

@my_decorator
def my_functions():
print("I'm the function")

my_function()

# Decorator with parameters
def decorator_with_arguments(number):
def my_decorator(func):
@functools.wraps(func)
def function_that_runs_func(*args, **kwargs):
print("In the decorator")
if number == 56:
print("Not running the function")
else:
func(*args, **kwargs)
print("After the decorator")
return function_that_runs_func
return my_decorator

@decorator_with_arguments(57)
def my_function_too(x, y):
print (x+y)

my_function_too(57, 67)

No comments:

Post a Comment