If you have ever wondered what those @something
mean above a python function or method then you are going to have your answers now. This @something
line of code is actually called a decorator. I have red from various articles about them but some of them were not able to clarify the concept of a decorator and what we can achieve with them. So in this post we'll learn a lot about python decorators. Here is a list of topics we'll be covering.
What is python decorator
A python decorator is nothing but a function which accepts your given function as a parameter and returns a replacement function. So its like something this
def decorator(your_func): def replacement(your_func_args): #do some other work return replacement @decorator your_func(your_func_args): #your_func codeNow when
your_func
gets called then the function which actually gets executed will be replacement
. Note that the parameter pattern of the replacement function must be same to that of your function. To understand what is happening, lets read the next section.
Understanding the core concept
It is very common in python to assign function objects, to pass function objects as arguments and to call functions using function refernces. What all these mean is you can do all of the following in python.
def any_func(): #do some work your_func = any_func your_func()Python decorators are based on the above concept and using a python decorator is equivalent to writing the following code.
def decorator(func): def replacement(args): #do some other work return replacement def your_func(args): #do some work your_func = decorator(your_func) #assign the replacement function to your given function. So now your_func points to replacement your_func(args) #call the replacement functionAs you can see, a decorator does nothing special, it just overrides your given function definition. And whenever
your_func
will be called then replacement
will be executed. Lets see a simple example now which is very helpful while understanding decorators.
def encoder(func): def decorator(html_string): #function which replace < and > with html codes and calls the passed in function for formatting html_string = html_string.replace(r'<', r'<') html_string = html_string.replace(r'>', r'>') result = func(html_string) return result return decorator @encoder def para_wrapper(html_string): #our function which simply wraps the html string with a paragraph tag return '<p>{0}</p>'.format(html_string)In this example what is happening is we use a decorator
@encoder
which replaces the function para_wrapper
with decorator
. Just remember that before the decorator assigns the new function to your function it gets the original copy of the function via the func
argument. So we can call our original function using that reference. Now look what decorator
is doing. It firstly makes the replacements in the html_string and then call the original function for its formatted output because that is what we want to send to the caller. Then it returns the output which our function generates for the new html_string. It is one of the use of the decorators. We'll see in the later section what other purposes can a decorator be used for.
Multiple decorators(Nested)
Decorators can easily be nested for multi-level uses. Nesting the decorator simply means nesting the function references.Lets see how nested decorator look
@decorator3 @decorator2 @decorator1 def your_func(args): #do some work #This is equivalent to the following code your_func = decorator3(decorator2(decorator1(your_func)))Nested decorators are helpful when there is one decorator which you can't modify and you want to add some input/output formatting or something then the easy way to do this is to add one more decorator.
Class method decorators
Class method decorators are used for class methods. These decorators are not any special ones. As i mentioned earlier that the parameter list of the replacement function must be same as the given function, in a class method decorator the first parameter of the replacement function is self
which is the reference to the current object so you can use current objects properties inside the replacement function if you want. All other things are the same. Lets see some skeleton code
def greet(func): def replacement(self): return 'Hello'+func() return replacement class Person(object): def __init__(self, args): #initialisation @greet def name(self): return self.firstname +' '+ self.lastname
Where can we use decorators
Python decorators can be used for a lot of scenarios. Some of them are
- Input Verification
- Input modification
- Output Modification
def your_decorator(your_func): def replacement(your_func_args): #do the input verification work. if(verification_pass): return your_func(your_func_args) else: return #You can return anything you want like an error message or raise an exception return replacement @your_decorator def your_func(your_func_args): #do what you want with the verified inputInput Modification: We have already seen an example of this in the previous section where we wrote a decorator to replace the < and > symbols with html codes.
Output Modification Now suppose you want to modify the output returned by your function. For example, your function returns a data dictionary but you want to add some default keys to the data dictionary before returning the output. Something like in a client-server session where you want to add session id and expire time in the data dictionary. Now you can add those fields right in your function but as the number of function grows this becomes tedious and we won't want to repeat the code. So lets write a decorator to add the data to the output of your function.
def response(func): def decorator(func_args): result = func(func_args) result['session_id'] = key result['expire_time'] = time return result return decorator @resposne def your_func(your_func_args): data = {} #fill data dictionary return dataThese were some simple cases where we can use decorators. There are some advanced uses of decorator also but those are only the modification of the above cases.
Nice article :D
ReplyDelete