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"
Python sees
@uppercase_decorator
and applies it to say_hello
.
uppercase_decorator(say_hello)
is called automatically.
It returns the
wrapper()
function that modifies say_hello()
output.
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. |