Python Decorators

Python Decorators

Decorators are a powerful feature in Python that allows you to modify the behavior of functions or classes without changing their source code.

Basic Decorator Pattern

A decorator is simply a function that takes another function as an argument and returns a modified version of it.

def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before function call")
result = func(*args, **kwargs)
print("After function call")
return result
return wrapper

@my_decorator
def say_hello(name):
print(f"Hello, {name}!")

# Usage
say_hello("World")

Common Use Cases

1. Timing Functions

import time

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

2. Caching/Memoization

from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)

3. Authentication

def require_auth(func):
def wrapper(user, *args, **kwargs):
if not user.is_authenticated:
raise PermissionError("Authentication required")
return func(user, *args, **kwargs)
return wrapper

Decorators with Arguments

To create decorators that accept arguments, you need an extra layer of nesting:

def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator

@repeat(3)
def greet():
print("Hello!")

Best Practices

<p>Always use &lt;code&gt;functools.wraps&lt;/code&gt; to preserve the original function's metadata.</p>
from functools import wraps

def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper