📌 What is a Python Decorator?

A Python decorator is a function that modifies another function or class without changing its original code.

READ complete article on this

It adds extra functionality (e.g., logging, authentication, caching) to functions or methods.

Uses the @decorator_name syntax to wrap a function.


✅ Basic Example of a Python Decorator

📌 Without a decorator (Manual way):

def uppercase_decorator(func):
    def wrapper():
        result = func()
        return result.upper()
    return wrapper

def say_hello():
    return "hello world"

# Manually applying decorator
say_hello = uppercase_decorator(say_hello)

print(say_hello())  # Output: "HELLO WORLD"

Problem: We have to manually wrap say_hello inside uppercase_decorator.


📌 With a decorator (Cleaner way):

def uppercase_decorator(func):
    def wrapper():
        result = func()
        return result.upper()
    return wrapper

@uppercase_decorator  # ✅ This automatically applies the decorator
def say_hello():
    return "hello world"

print(say_hello())  # Output: "HELLO WORLD"

Why is this better?

  • Less manual wrapping.
  • Easier to read and maintain.

✅ How Decorators Work (Step-by-Step)

@uppercase_decorator
def say_hello():
    return "hello world"

1️⃣ Python sees @uppercase_decorator and applies it to say_hello.

2️⃣ uppercase_decorator(say_hello) is called automatically.

3️⃣ It returns the wrapper() function that modifies say_hello() output.

4️⃣ say_hello() now returns "HELLO WORLD" instead of "hello world".


✅ Real-World Examples of Decorators

🔹 Example 1: Logging Decorator

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with {args} {kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned {result}")
        return result
    return wrapper

@log_decorator
def add(a, b):
    return a + b

add(5, 3)

💡 Output:

Calling add with (5, 3) {}
add returned 8

Why Use This?

  • Automatically logs function calls and results without modifying the function itself.

🔹 Example 2: Authentication Decorator in FastAPI

from fastapi import FastAPI, Depends, HTTPException

app = FastAPI()

def auth_required(func):
    def wrapper(username: str):
        if username != "admin":
            raise HTTPException(status_code=403, detail="Unauthorized")
        return func(username)
    return wrapper

@app.get("/secure-data")
@auth_required  # ✅ Protects this route
def secure_data(username: str):
    return {"message": "Secure data accessed!"}

# Now only "admin" can access this route

Why Use This?

  • Ensures only authenticated users can access certain API routes.

🔹 Example 3: Time Execution Decorator

import time

def time_it(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} took {end_time - start_time:.4f} seconds")
        return result
    return wrapper

@time_it
def slow_function():
    time.sleep(2)
    return "Finished"

slow_function()

💡 Output:

slow_function took 2.0001 seconds

Why Use This?

  • Measures execution time of a function (useful for performance optimization).

✅ Summary: Why Use Python Decorators?

Feature Why It's Useful?
Code Reusability Add extra behavior without modifying function code.
Readability @decorator_name makes it clear that the function is modified.
Flexibility Can be applied to multiple functions easily.
Used in Frameworks FastAPI, Django, Flask, TensorFlow all use decorators.