Decorators
WHAT?
A decorator in Python is a special kind of function that adds extra functionality to another function, without changing the original function’s code.
WHY?
To add some functionality before or after a function is executed as a wrapper around the original function
To create a standardised pattern for reusability across various functions
To follow DRY ( Do not repeat yourself ) in the code
HOW?
Example 1: A simple function with no decorators
def greet():
print("Hello!")
A sample decorator function
def log_function_call(func):
def wrapper():
print(f"Starting function {func.__name__}")
func()
print(f"Finished function {func.__name__}")
return wrapper
Use the decorator by adding "@" before the decorator function and add it above the function definition you want to wrap it with
@log_function_call
def greet():
print("Hello!")
Call the greet function
greet()
greet function call execution result
Starting function greet
Hello!
Finished function greet
Example 2: Decorators with Arguments *args and **kwargs
Decorator function with args and kwargs
def log_function_call(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with:")
print(f"Positional arguments: {args}")
print(f"Keyword arguments: {kwargs}")
result = func(*args, **kwargs)
print("Done.")
return result
return wrapper
Original function definition with the decorator function added
@log_function_call
def create_order(customer_name, *items, delivery=False, **extras):
print(f"Creating order for: {customer_name}")
print(f"Items: {items}")
print(f"Delivery requested? {'Yes' if delivery else 'No'}")
print(f"Extra options: {extras}")
return "Order Created"
Call the original function
create_order(
"Alexander",
"Laptop", "Mouse", "Charger",
delivery=True,
gift_wrap=True,
note="Deliver after 1 pm"
)
Function call execution result
Calling create_order with:
Positional arguments: ('Alexander', 'Laptop', 'Mouse', 'Charger')
Keyword arguments: {'delivery': True, 'gift_wrap': True, 'note': 'Deliver after 1 pm'}
Creating order for: Alexander
Items: ('Laptop', 'Mouse', 'Charger')
Delivery requested? Yes
Extra options: {'gift_wrap': True, 'note': 'Deliver after 1 pm'}
Done.
Example 3: Pass a function as an argument to a decorator
Scenario: Role-Based Access Control (RBAC)
You’re building an app with different users: admin, manager, and guest.
You want to restrict access to some functions based on role.
So, let’s:
1. Write a check_permission function
2. Pass it into a decorator
3. Let the decorator use that to decide whether to run the original function
Define permission check functions
def admin_only(user):
return user.get("role") == "admin"
def manager_or_admin(user):
return user.get("role") in ("admin", "manager")
Create the decorator that accepts a permission function
def authorize(permission_function):
def decorator(original_function):
def wrapper(user, *args, **kwargs):
if permission_function(user):
print(f"Access granted to {user['name']}")
return original_function(user, *args, **kwargs)
else:
print(f"Access denied for {user['name']} ({user['role']})")
return wrapper
return decorator
Decorate your protected functions
@authorize(admin_only)
def delete_user(user, target_username):
print(f"{user['name']} deleted user: {target_username}")
@authorize(manager_or_admin)
def view_reports(user):
print(f"{user['name']} is viewing reports.")
Calling the function
admin_user = {"name": "Alice", "role": "admin"}
manager_user = {"name": "Bob", "role": "manager"}
guest_user = {"name": "Eve", "role": "guest"}
delete_user(admin_user, "John")
delete_user(guest_user, "John")
view_reports(manager_user)
view_reports(guest_user)
Function call result
Access granted to Alice
Alice deleted user: John
Access denied for Eve (guest)
Access granted to Bob
Bob is viewing reports.
Access denied for Eve (guest)