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)