LEARN TO CREATE AND USE PYTHON DECORATORS
If you just started using python or django you may have encoutered some functions that have an @ sign followed by a name just before their definition. In python world this is a syntax to declare that a function is wrapped inside another function (the later being called a decorator).
Small recap
To try understanding decorators let's remember some things about functions and variables in python:
a function is something that generates a value based on the values of its arguments.
functions are first-class objects
functions create a new scope
*args and **kwargs are available in function scope
variable resolution order is done via the LEGB* rule
variables have a lifetime
* LEGB rule reffers to Local, Enclosing(function), Global (module), Built-in (python)
Also you should know that in python you can have nested functions, that means we can declare functions inside of functions. They are used mostly because they offer encapsulation and closures (causes the inner function to remember the state of its environment when called).
Definition of a decorator
A proper definition of a decorator can be as following: a decorator is just a callable that takes a function(usually) as an argument and returns a function object(usually). Their main purpose is to add features to the original function. It helps reducing boilerplate code and also with separation of concerns. They enhance readability and maintainability and they are explicit.
Example:
def verbose(original_function):
def new_function(*args, **kwargs):
print("Entering", original_function.__name__)
res = original_function(*args, **kwargs)
print("Exiting ", original_function.__name__)return res
return new_function
Same example as above but decorator is defined as class:
class verbose():
def __init__(self, original_function):
self.original_function = original_function
def __call__(self, *args, **kwargs):
print("Entering", self.original_function.__name__)
res =self.original_function(*args, **kwargs)
print("Exiting ", self.original_function.__name__)
return res
How to invoke the decorator
There are 2 options to invoke a decorator:
Just by passing a function as input to the decorator function and get back the “enhanced” version of it
Using decoration syntax @ - is basically the same as the above, but it adds syntactic sugar
# first variant def add1(a, b):return a+b
verbose(add1)(4, 5)# second variant @verbosedef add2(a, b):return a+b
# implies automatic call of the decorated function
add2(4, 5)
Passing arguments to a decorator
Since a decorator must accept a function as an argument, you cannot pass the arguments directly to the decorator. To solution is quite simple, just create a normal function that returns a decorator.
Here is a sample of decorator that adds execution time if it's specificed by the parameter:
def verbose(include_execution_time=False):def verbose(original_function):# make a new function that prints a message # when original_function starts and finishesdef new_function(*args, **kwargs):print "Entering", original_function.__name__
t = time.clock() if include_execution_time else None
original_function(*args, **kwargs)print "Exiting ", original_function.__name__,\
" (execution time: {0})".format(time.clock()-t) if t else ""return new_function
return verbose
Decorator types
Function aren't the only ones that can be decorated, here are other types of decorators:
Method decorators
@classmethod: the class of the object instance is implicitly passed as the first argument instead of self. This can be useful for having a different constructor (eg. date instances can be created with datetime.date(year, month, day), but you can also use classmethod date.fromtimestamp(timestamp) )
@staticmethod: neither self (the object instance) nor cls (the class) is implicitly passed as the first argument (behave like plain functions)
Class decorators
Even a decorator for decorators (sounds like inception)
Chain decorators
Sample of method decorators:
class Example(object): name = "Example"
@classmethod
def say_my_name(cls):print "%s class method called" % cls.name @staticmethod
def say_name(name):
print "%s static method called" % name # no need for an instance
Example.say_my_name()
Example.say_name('Other name')
Sample of class decorator:
def verbose(original_function):
# make a new function that prints a message
# when original_function starts and finishes
def new_function(*args, **kwargs):
print "Entering ", original_function.__name__ original_function(*args, **kwargs)
print "Exiting ", original_function.__name__ return new_function def class_verbose(cls):
"The class decorator example"
class NewClass(cls):
"This is the overwritten class"
def __getattribute__(self, attr_name): obj = super(NewClass, self).__getattribute__(attr_name)
if hasattr(obj, '__call__'):
return verbose(obj)
return obj return NewClass
@class_verbose
class A(object):
def say_hi(self):
print "Hi"
Where they can be useful? There are many places where decorators are useful, here is a short list:
logging
benchmark
type checking
synchronization (two or more functions on a given lock.)
memoize (caches a function's return value)
django (authentication, view permission and caching, etc.)
many others
Stackoverflow reference For deeper undestanding and other example you can visit the folowing link. You can also contact us for a more detailed discussion. You can find other interesting articles on similar topics here.
Comments