Decorators are a very powerful and useful tool in Python since it allows programmers to modify the behaviour of a function or class. Decorators allow us to wrap another function in order to extend the behaviour of the wrapped function, without permanently modifying it. But before diving deep into decorators let us understand some concepts that will come in handy in learning the decorators.
First Class Objects
In Python, functions are first class objects which means that functions in Python can be used or passed as arguments.
Properties of first class functions:
- A function is an instance of the Object type.
- You can store the function in a variable.
- You can pass the function as a parameter to another function.
- You can return the function from a function.
- You can store them in data structures such as hash tables, lists, …
Consider the below examples for better understanding.
Example 1: Treating the functions as objects.
Python3
def shout(text):
return text.upper()
print (shout( 'Hello' ))
yell = shout
print (yell( 'Hello' ))
|
Output:
HELLO
HELLO
In the above example, we have assigned the function shout to a variable. This will not call the function instead it takes the function object referenced by a shout and creates a second name pointing to it, yell.
Example 2: Passing the function as an argument
Python3
def shout(text):
return text.upper()
def whisper(text):
return text.lower()
def greet(func):
greeting = func( )
print (greeting)
greet(shout)
greet(whisper)
|
Output:
HI, I AM CREATED BY A FUNCTION PASSED AS AN ARGUMENT.
hi, i am created by a function passed as an argument.
In the above example, the greet function takes another function as a parameter (shout and whisper in this case). The function passed as an argument is then called inside the function greet.
Example 3: Returning functions from another function.
Python3
def create_adder(x):
def adder(y):
return x + y
return adder
add_15 = create_adder( 15 )
print (add_15( 10 ))
|
Output:
25
In the above example, we have created a function inside of another function and then have returned the function created inside.
The above three examples depict the important concepts that are needed to understand decorators. After going through them let us now dive deep into decorators.
Decorators
As stated above the decorators are used to modify the behaviour of function or class. In Decorators, functions are taken as the argument into another function and then called inside the wrapper function.
Syntax for Decorator:
@gfg_decorator
def hello_decorator():
print("Gfg")
'''Above code is equivalent to -
def hello_decorator():
print("Gfg")
hello_decorator = gfg_decorator(hello_decorator)'''
In the above code, gfg_decorator is a callable function, that will add some code on the top of some another callable function, hello_decorator function and return the wrapper function.
Decorator can modify the behaviour:
Python3
def hello_decorator(func):
def inner1():
print ( "Hello, this is before function execution" )
func()
print ( "This is after function execution" )
return inner1
def function_to_be_used():
print ( "This is inside the function !!" )
function_to_be_used = hello_decorator(function_to_be_used)
function_to_be_used()
|
Output:
Hello, this is before function execution
This is inside the function !!
This is after function execution
Let’s see the behaviour of the above code and how it runs step by step when the “function_to_be_used” is called.


Let’s jump to another example where we can easily find out the execution time of a function using a decorator.
Python3
import time
import math
def calculate_time(func):
def inner1( * args, * * kwargs):
begin = time.time()
func( * args, * * kwargs)
end = time.time()
print ( "Total time taken in : " , func.__name__, end - begin)
return inner1
@calculate_time
def factorial(num):
time.sleep( 2 )
print (math.factorial(num))
factorial( 10 )
|
Output:
3628800
Total time taken in : factorial 2.0061802864074707
What if a function returns something or an argument is passed to the function?
In all the above examples the functions didn’t return anything so there wasn’t an issue, but one may need the returned value.
Python3
def hello_decorator(func):
def inner1( * args, * * kwargs):
print ( "before Execution" )
returned_value = func( * args, * * kwargs)
print ( "after Execution" )
return returned_value
return inner1
@hello_decorator
def sum_two_numbers(a, b):
print ( "Inside the function" )
return a + b
a, b = 1 , 2
print ( "Sum =" , sum_two_numbers(a, b))
|
Output:
before Execution
Inside the function
after Execution
Sum = 3
In the above example, you may notice a keen difference in the parameters of the inner function. The inner function takes the argument as *args and **kwargs which means that a tuple of positional arguments or a dictionary of keyword arguments can be passed of any length. This makes it a general decorator that can decorate a function having any number of arguments.
Chaining Decorators
In simpler terms chaining decorators means decorating a function with multiple decorators.
Example:
Python3
def decor1(func):
def inner():
x = func()
return x * x
return inner
def decor(func):
def inner():
x = func()
return 2 * x
return inner
@decor1
@decor
def num():
return 10
@decor
@decor1
def num2():
return 10
print (num())
print (num2())
|
Output:
400
200
The above example is similar to calling the function as –
decor1(decor(num))
decor(decor1(num2))
Whether you're preparing for your first job interview or aiming to upskill in this ever-evolving tech landscape,
GeeksforGeeks Courses are your key to success. We provide top-quality content at affordable prices, all geared towards accelerating your growth in a time-bound manner. Join the millions we've already empowered, and we're here to do the same for you. Don't miss out -
check it out now!
Last Updated :
23 Jan, 2023
Like Article
Save Article