Python Decorators
- It’s a very powerful and useful tool in Python
- It allows to
add functionalityto anexisting function or class.
def make_pretty(func):
def inner():
print("Inner function")
func()
return inner
# Without decorator
def ordinary():
print("Ordinary function")
pretty = make_pretty(ordinary)
pretty()
# With decorator(Using Closure)
@make_pretty
def ordinary():
print("Ordinary function")
ordinary()
# Inner function
# Ordinary function
Fix meta data for the function - @warps
def deco(func):
def inner_deco():
print("this is the start of my fun")
func()
print("this is the end of my fun")
return inner_deco
@deco
def test1():
print(4+5)
test1()
print(test1.__name__)
# this is the start of my fun
# 9
# this is the end of my fun
# inner_deco --> meta data changes - should be test1
Update decorator function to below
from functools import wraps
def deco(func):
@wraps(func)
def inner_deco():
print("this is the start of my fun")
func()
print("this is the end of my fun")
return inner_deco
Nested Decorators
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
print(num())
# d1 = decor(num)
# d2 = decor1(d1)
# d2()
# Output: 400
Decorator without parameters
def outer(*args, **kwargs):
print("Outer")
def inner(func):
print("inner--", args[0])
func()
return inner
@outer('Amrit')
def my_func():
print("my function")
# -- fxn call not required
Decorator with parameters
def outer(func): # --
print("Outer")
def inner(*args, **kwargs): # --
print("inner--", args[0])
func(*args, **kwargs) # --
return inner
@outer
def my_func(var): # --
print("my function")
my_func("Amrit") # --
# Both has same Answer
Returning Values From Decorated Functions
import functools
def Outer(func):
@functools.wraps(func)
def inner(*args, **kwargs):
res = func(*args, **kwargs)
print("Inner")
return res
return inner
@Outer
def sum(a,b):
# print(a+b)
return a+b
print(sum(1,2))
Decorators With Arguments –> args, kwargs
# Divide by 0 -- Error
def Outer(fxn):
print("Outer")
def inner(*args, **kwargs):
if args[1] == 0:
# raise Exception("b cant be 0")
print("b can't be zero")
return
fxn(*args, **kwargs)
return inner
@Outer
def main(a, b):
print(a/b)
main(1,0)
# Calculate Execution time
# Using Decorator
# Also return value from fxn
import time
import functools
def Outer(func):
@functools.wraps(func)
def inner(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print("Execution time: ", end-start)
return res
return inner
@Outer
def sum(a,b):
time.sleep(2)
# print(a+b)
return a+b
print(sum(1,2))
# Repeat N times
import functools
def repeat(num_times):
def decorator_repeat(func):
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
# value = func(*args, **kwargs)
# return value
return wrapper_repeat
return decorator_repeat
@repeat(num_times=4)
def greet(name):
print(f"Hello {name}")
greet("Amrit-")
Example - Logger Decorator
from functools import wraps
def log_activity(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"🚀 Calling: {func.__name__}")
result = func(*args, **kwargs)
print(f"✅ Finished: {func.__name__}")
return result
return wrapper
@log_activity
def brew_chai(type, milk="no"):
print(f"Brewing {type} chai and milk status {milk}")
brew_chai("Masala")
Example - Auth Decorator
from functools import wraps
def require_admin(func):
@wraps(func)
def wrapper(user_role):
if user_role != "admin":
print("Access denied: Admins only")
return None
else:
return func(user_role)
return wrapper
@require_admin
def acess_tea_inventory(role):
print("Access granted to tea inventory")
acess_tea_inventory("user")
acess_tea_inventory("admin")