Data Science and Machine Learning Internship ...
- 22k Enrolled Learners
- Weekend/Weekday
- Live Class
Decorators in Python are powerful tools in Python. It allows programmers to modify the behaviour of function or class. Decorators allow to wrap another function to extend the behaviour of the wrapped function, without modifying it. This article will brief the following topics.
Today, we are going to discuss very integral of python i.e. decorators. Decorators can be thought of as a very useful and powerful tool but only if used properly. In simple words: they are functions which modify the functionality of other functions. They help to make our code shorter and more Pythonic.
Definition:
A decorator is a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate.
Warning: They are so confusing at first sight, So, tighten your belts because we are going for a deep dive!
Before diving straight, we will take a look at a few of the things that you should know prior to understanding Decorators.
Functions in Python are First Class Citizens. This means that they support operations such as being passed as an argument, returned from a function, modified, and assigned to a variable.
Now, let us look into Function that can be passed as an argument
You must know about this and if you do not then firstly visit the following link and then come back.
Now, As functions are objects and functions accept objects as arguments, We may conclude that we can pass function to a function. Let’s understand it in a better way with some code.
Due to the fact that every parameter of a function is a reference to an object and functions are objects as well, we can pass functions – or better “references to functions” – as parameters to a function.
def greet(name): return “Hey {}”.format(name) def i_will_greet(greet,name=”Avikar): print(greet(name)) print(“Welcome to the world of edureka”) i_will_greet(greet)
As we can clearly see that function greet() has been passed as a parameter to the function i_will_greet().
//Output:
Hey Pallavi
Welcome to the world of edureka
Note: The function greet has been passed to i_will_greet with no parentheses. Because if you use parenthesis then it will not pass function but its definition.
Let’s consider one more example to make our basics concrete.
from math import sin,cos def calculate(func): res=0 for i in [1,2,3,4,5]: res+=func(i) return res print(“the value of sin is {}”.format(calculate(sin))) print(“the value of cos is {}”.format(calculate(cos)))
//Output:The value of sin is 0.1761616497223787
the value of cos is -1.2358184626798336
Let us look into the second Function that can be defined within functions
The concept of having or defining functions inside a function is completely new to C or C++ programmers. We already know about nesting and surprisingly we can nest our functions too i.e. we can define as many functions as we want within a function.
def tell_me(): def question(): print("is python boring") def answer(): print("Definintely not. Silly!") question() answer() return "look now you know what to learn"
//Output:is python boring?
Definitely not. Silly!
look now you know what to learn
As it can be easily understood that when tell_me() is called
To have a firm grasp, Let us consider one more example in which we are required to convert temperature from Celsius to Fahrenheit.
def temperature(t): def celsius2fahrenheit(x): return 9 * x / 5 + 32 return (“It’s ”+str(celsius2fahrenheit(t)+” degrees! ” print(temperature(35))
//Output:95.0 degrees
Let us move onto the third Function that can return functions as well.
It is not necessary to execute a function within another function, we can return it as an output as well.
def bake_cake(flavor="chocolate"): print("place the base of cake and decorate it with {} and fruits".format(flavor)) def cherry(): return "place cherry at the top of your {} cake".format(flavor) return cherry tasty = bake_cake("butterscotch") print(tasty())
Output:place the base of the cake and decorate it with butterscotch and fruits
place cherry at the top of your butterscotch cake
Let us consider a useful example in which we assume that we can pass any number of arguments(*args). For a better understanding visit Edureka Community.
We can generalize our factory function so that it can work for polynomials of arbitrary degree
def polynomial_creator(*coeff): def polynomial(x): for index,c in enumerate(coeff[::-1]): res+=c*x**index return res return polynomial
The above polynomial_creator function when passed with coefficients of quadratic equation,
Now if we can assign this returned function to a variable(as studied Earlier), we can directly use our wrapped function.
p1 = polynomial_creator(2, 3,-1) p2 = polynomial_creator(1, 8, -1, 3, 2) for x in range(-2, 2, 1): print(x, p1(x), p2(x))
Note that when p1(x) is called the coeff. are 2,3 and -1 which indicates that the polynomial() is defined when the polynomial_creator() is assigned to p1 or p2, but not called.
So when we call p1() or p2(), we will pass the arguments to the polynomial(x).
As we can see from the output,
//Output:-2 1 -56
-1 -2 -9
0 -1 2
1 4 13
Now let’s look into the last Function that can be assigned to a variable
Did you notice tasty=bake_cake(“butterscotch”) in above code? Yes, right! We can assign functions to variables too i.e. we create an alias of functions.
The usefulness of the above assignment is:
Even if you delete the function, you can still use it via the variable you created.
Try del bake_cake
With all this knowledge in our cylinder, we are ready for the dive. But, the drive wouldn’t last long if we do not have a diving mask(the very small but important part).
Closure (our diving mask)
Python allows a nested function to access the outer scope of the enclosing function. This is a critical concept in decorators.
For example, let us consider an outer and inner function with their variables.
def outer(): var =10 def inner(): varb=30 print(“value of var inside inner {}”.format(var)) print(“value of varb inside inner is {}”.format(varb)) inner() print(“value of var outside inner {}”.format(var)) print(“value of var_b outside inner is {}”.format(varb))
//Output:value of var inside inner 10
value of var_b inside inner is 30
value of var outside inner 10
Traceback (most recent call last):
File “<pyshell#5>”, line 1, in <module>
outer()
Moving on to Our First Decorator
To create a decorator:
To understand it properly, lets first consider an example:
def outer(func): print(“inside outer”) def inner() print("inside inner function”) func() print(“inside inner again”) return inner
We have here defined inner() inside outer(). A function func is to be passed to the function as a parameter.
Now,
def need_decoration(): print("inside function call") need_decoration=outer(need_decoration) inside outer need_decoration() inside inner function inside function call inside inner again
It’s easy to understand. RIGHT!
Here we have called outer() with function need_decoration() as its parameter. So when outer is called the first print statement is executed and a function inner() is defined with func as need_decoration and inner function is returned.
This returned function is stored in need_decoration.
Now, when need decoration is executed via parenthesis inner function is called.
Two important things you need to understand :
1. The difference between a function call and function definition.
import math temp=math.sin #temp is assigned the function definition temp(4) -0.7568024953079282 temp=math.sin(4) #temp is assigned the value returned by function print(temp) -0.7568024953079282
2. One might think that as need_decoration has lost its original definition and assigned to inner(), what will happen when need_decoration is called from inner()?
If you are thinking that it may end into an endless loop where inner() calls func() as need_decoration() and need_decoration is again equivalent to calling inner() then trust me you are not the only one in this dilemma.
This is why decorators turn bit clumsy and tough to understand.
So, This does not happen at all. When you define inner via calling outer as
need_decoration=outer(need_decoration) inner() automatically stores the definition of need_decoration there. We will delve deeper into this later.
You must be thinking that where is “ @ “, the glory of decorators. You will be glad that you have already used it but in a hidden way.
need_decoration=outer(need_decoration)
can be replaced by
@outer above the definition of need_decoration. So after rewriting the above code
@outer def need_decoration(): print("inside function call") inside outer need_decoration() inside inner function inside function call inside inner again
We can do a lot with decorators like Multiple decorators can be applied to a single function. But we will study this in the next article.
This brings us to the end of this article where we have learned how we can use Decorators in python with several examples. I hope you are clear with all that has been shared with you in this tutorial.
If you found this article on “Decorators In Python” relevant, check out the Edureka Python Certification Training, a trusted online learning company with a network of more than 250,000 satisfied learners spread across the globe.
We are here to help you with every step on your journey and come up with a curriculum that is designed for students and professionals who want to be a Python developer. The course is designed to give you a head start into Python programming and train you for both core and advanced Python concepts along with various Python frameworks like Django.
If you come across any questions, feel free to ask all your questions in the comments section of “Decorators In Python” and our team will be glad to answer.
Course Name | Date | Details |
---|---|---|
Python Programming Certification Course | Class Starts on 7th December,2024 7th December SAT&SUN (Weekend Batch) | View Details |
Python Programming Certification Course | Class Starts on 28th December,2024 28th December SAT&SUN (Weekend Batch) | View Details |
edureka.co