stackademic

The leading education platform for anyone with an interest in software development.

Decorators

Wrap functions to add logging, caching, auth, and cross-cutting behavior

Overview

Decorators are functions that take a callable and return a new callable, usually extending behavior without modifying the original function body. The @decorator syntax is syntactic sugar for func = decorator(func).

Syntax / Usage

import functools

def log_calls(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

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

# Decorator with arguments
def repeat(times):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def greet():
    print("Hi")

Built-in decorators include @property, @staticmethod, @classmethod, and @functools.lru_cache.

Examples

Simple timing decorator:

import time
import functools

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"{func.__name__} took {elapsed:.4f}s")
        return result
    return wrapper

@timer
def fetch_data():
    time.sleep(0.1)
    return [1, 2, 3]

Require authentication (conceptual):

def require_auth(handler):
    @functools.wraps(handler)
    def wrapper(request, *args, **kwargs):
        if not request.user.is_authenticated:
            raise PermissionError("Login required")
        return handler(request, *args, **kwargs)
    return wrapper

Common Mistakes

  • Forgetting @functools.wraps, losing __name__ and docstrings
  • Decorating functions that need specific signatures without *args, **kwargs
  • Stacking decorators in the wrong order—inner decorators run first
  • Using decorators for heavy logic that belongs in the function itself

See Also

python-functions python-classes python-context-managers