Decorators are one of the most powerful concepts in python. A decorator is a function which takes another function or wraps a function to improve the behaviour of that function.
A decorator is a function which takes another function
and extends its functionality without actually modifying it and finally
returns it.
In this tutorial, we will learn how to write decorators
and why we should use it?
A decorator in Python is any callable Python object that is used to modify a function or a class.
def greet(name):
print('hello ' + name)
greet('abhishek')
wish = greet
wish('abhishek')
Output
hello abhishek hello abhishek
def greet(func, name):
func(name)
def sayHello(name):
print('Hello ' + name)
def sayBye(name):
print('Bye ' + name)
greet(sayHello, 'abhishek')
greet(sayBye, 'abhishek')
Such functions are known as higher-order functions.
Output
Hello abhishek Bye abhishek
def greet(name):
def sayHello():
print('Hello ' + name)
return sayHello
wish = greet('abhishek')
wish()
Output
Hello abhishek
These are basics, which are required in order to understand decorators in python.
Basically, decorators are also a function but they accept functions as arguments and may perform some modifications to it.
def make_pretty(func):
def inner():
print('*************************')
func()
print('*************************')
return inner
def sayHello():
print('Hello world')
decorated_func = make_pretty(sayHello)
decorated_func()
Output
************************* Hello world *************************
Here, you can observe that we are taking a function as an argument and defining a completely new function by calling that function and finally returning our new function which can be used now as a new version of our function.
We can use @
symbol with the name of
decorator function to decorate our functions and it works in the same way.
@decorator_func_name
def func_name():
....
func_name() # calling
Using the syntactic sugar for decorators
def make_pretty(func):
def inner():
print('*************************')
func()
print('*************************')
return inner
@make_pretty
def sayHello():
print('Hello world')
sayHello()
Output
************************* Hello world *************************
def repeat_twice(func):
def inner(name):
func(name)
func(name)
return inner
@repeat_twice
def greet(name):
print('Welcome ' + name)
greet('Abhishek')
Output
Welcome Abhishek Welcome Abhishek
def smart_greet(func):
def inner(name):
if name == 'Abhi':
print('Welcome ' + name)
else:
print('Welcome guest!')
return inner
@smart_greet
def greet(name):
print('Welcome ' + name)
greet('Abhi')
greet('Abhishek')
Output
Welcome Abhi Welcome guest!
def smart_divide(func):
def inner(a, b):
if b == 0:
print('Unable to divide!')
else:
func(a, b)
return inner
@smart_divide
def divide(a, b):
print(a // b)
divide(10, 0)
divide(10, 5)
Output
Unable to divide! 2
We can also return some values from a function which is being decorated.
def returning_func(func):
def inner(name):
return func(name)
return inner
@returning_func
def greet(name):
return 'Welcome ' + name
print(greet('Abhishek'))
Output
Welcome Abhishek
We can even chain multiple decorators to a function. They will be executed sequentially in order.
def star(func):
def inner(name):
print('*' * 20)
func(name)
print('*' * 20)
return inner
def percent(func):
def inner(name):
print('%' * 20)
func(name)
print('%' * 20)
return inner
@star
@percent
def greet(name):
print('Welcomme ' + name)
greet('Abhi')
Output
******************** %%%%%%%%%%%%%%%%%%%% Welcome Abhi %%%%%%%%%%%%%%%%%%%% ********************
def star(func):
def inner(name):
print('*' * 20)
func(name)
print('*' * 20)
return inner
def percent(func):
def inner(name):
print('%' * 20)
func(name)
print('%' * 20)
return inner
@percent
@star
def greet(name):
print('Welcome ' + name)
greet('Abhi')
Output
%%%%%%%%%%%%%%%%%%%% ******************** Welcome Abhi ******************** %%%%%%%%%%%%%%%%%%%%
We will rewrite following function decorator as class decorator.
def greet(func):
def inner():
print('*' * 20)
func()
print('*' * 20)
return inner
@greet
def greet_abhi():
print('Welcome Abhi')
greet_abhi()
Output
******************** Welcome Abhi ********************
Following decorator works similar as above but uses class instead of function.
class greet:
def __init__(self, func):
self.func = func
def __call__(self):
print('*' * 20)
self.func()
print('*' * 20)
@greet
def greet_abhi():
print('Welcome Abhi')
greet_abhi()
Output
******************** Welcome Abhi ********************