Concepts - Decorators in Python

Published on
3 mins read
––– views
Photo by Chris Ried on Unsplash
python-notes

Hope you have understood from the previous course Functions and OOPs, what is a function? Now, let us understand what is a higher order function.

A Higher Order function is a function, which is capable of doing any one of the following things:

  • It can be functioned as a data and be assigned to a variable.
  • It can accept any other function as an argument.
  • It can return a function as its result.

The ability to build Higher order functions, allows a programmer to create Closures, which in turn are used to create Decorators.

Decorators are evolved from the concept of closures.

  • A decorator function is a higher order function that takes a function as an argument and returns the inner function.

  • A decorator is capable of adding extra functionality to an existing function, without altering it.

Examples

Without decorator

def outer(x, y):
def inner1():
return x + y
def inner2():
return x * y
return inner1, inner2
f1, f2 = outer(10, 25)
print(f1()) # 35
print(f2()) # 250
def outer(x, y):
def inner1():
return x + y
def inner2(z):
return inner1() + z
return inner2
f = outer(10, 25)
print(f(15)) # 10 + 25 + 15 = 50 (result)

Problem while using decorator is the function name

def outer(func):
def inner():
print("Accessing: ", func.__name__)
return func()
inner.__name__ = func.__name__
return inner
@outer
def greet():
print("Hello!!")
print(greet.__name__) # greet

Using from functools import wraps for Function Name ref - Stack Overflow

from functools import wraps
def decorator_func(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@decorator_func
def square(x):
return x ** 2
print(square.__name__) # square
def bind(func):
func.data = 9
return func
@bind
def add(x, y):
return x + y
print(add(3, 10)) # 13
print(add.data) # 9

Example - 1

from functools import wraps
def star_print(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(5 * "*")
func(*args, **kwargs)
print(5 * "*")
return wrapper
def hash_print(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(5 * "#")
func(*args, **kwargs)
print(5 * "#")
return wrapper
@star_print
@hash_print
def greet(msg):
print(msg)
greet('hello')
print(greet.__name__)

Output

*****
#####
hello
#####
*****
greet

Example - 2

Function Input Data validation with decorators

from functools import wraps
def smart_divide(func):
@wraps(func)
def wrapper(*args):
a, b = args
if b == 0:
print('oops! cannot divide')
return
return func(*args)
return wrapper
@smart_divide
def divide(a, b):
return a / b
print(divide.__name__)
print(divide(4, 16))
print(divide(8, 0))
print(divide(8, 1))

Output

divide
0.25
oops! cannot divide
None
8.0