Decorators

Date: February 12th 2016
Last updated: February 9th 2017

This entry follows on from closures because they are the main underlying concept of decorators. Things to remember while considering decorators include scoping rules and nested functions. Also recall that functions can be passed as arguments to other functions, and a function can be returned.

I recommend reading the section on "returning functions" by Jeff Knupp. For a concise explanation of decorators read this article and the go on to read this article. There is a repository containing python decorators here.

Useful resources:

Basic structure:
The "@" symbol followed by the function name is used to implement the decorator. The decorator must be called immediately before the decorated function.

def wrapper(func):
    def main_function_call(*args):
        # modify some code
        # print some stuff
        # log some stuff etc
        # execute the function
        return func(*args)
    return main_function_call

# syntact sugar version of decorator
@wrapper
def new_func():
    # add some code

# the decoratation symbol is the same as this:
new_func = wrapper(new_func)

Examples

Logging decorator:

import logging
logging.getLogger().setLevel(logging.INFO)

def logger(func): #func is a callable
    # instantiate logging module
    logger = logging.getLogger(__name__)

    def inner(*args, **kwargs):
        logger.info('logging inner function')
        return func(*args, **kwargs)  #returns a callable

    return inner

@logger
def add(x, y):
    """docstring: add 2 arguments together"""
    return x + y

print("@logger: add(2,2) = %s \n" %add(2, 2))
print("@logger: add(2,4) = %s \n" %add(2, 4))

# returns:
#INFO:__main__:logging inner function
#@logger: add(2,2) = 4 
#
#INFO:__main__:logging inner function
#@logger: add(2,4) = 6

Check docstrings decorator:

def doccheck(f):
    if f.__doc__:
        print("%s" % f.__doc__)
    return f

@doccheck
def add(x, y):
    """docstring: add 2 arguments together"""
    return x + y

print("@doccheck: add(2,2) = %s \n" %add(2, 2))

# returns:
#docstring: add 2 arguments together
#@doccheck: add(2,2) = 4

strip argument decorator: use one argument

def strip_arg(func):
    def inner(*args):
        # isolate x argument
        x = args[0]
        # only x makes it to the function call
        # this makes add function use default y value
        return func(x)
    return inner

@strip_arg
def add(x, y=7):
    """docstring: add 2 arguments together"""
    return x + y

print("@strip_arg: add(2) = %s " %add(2))
print("@strip_arg: add(2,2) = %s " %add(2,2))
print("@strip_arg: add(2,8) = %s \n" %add(2,8))

# returns:
#@strip_arg: add(2) = 9 
#@strip_arg: add(2,2) = 9 
#@strip_arg: add(2,8) = 9

Currency decorator: adapted from here

def currency(f):
    def wrapper(f):
        return '$' + str(f)
    return wrapper

@currency
def price(value):
    return value

print(price(9))
#returns: $9

results matching ""

    No results matching ""