What is a Python Decorator?

πŸ“Œ What is a Python Decorator?

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

βœ… 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.

Leave a Comment