ReviseAlgo Logo

Functions

Decorators

Modifying function behavior with wrapper functions and @ syntax

Interview: Very important for interviews — used in web frameworks, caching, auth, and logging

Last Updated: June 12, 2026 12 min read

Decorators are a powerful Python feature that allows you to modify or extend the behavior of functions and methods without permanently changing their code. They use the @decorator syntax and are widely used in web frameworks (Flask, Django), testing, caching, and access control.

How Decorators Work

  • @ syntax is sugar: @decorator above a function is equivalent to func = decorator(func)
  • Decorator is a function: It takes a function as input and returns a new (modified) function
  • Wrapper function: The inner function that wraps the original, adding behavior before/after
  • functools.wraps: ALWAYS use @wraps(func) on the wrapper to preserve the original function's name, docstring, and annotations

Types of Decorators

  • Simple decorator: @timer — takes only the function as argument
  • Decorator with arguments: @repeat(3) — needs an extra level of nesting (decorator factory)
  • Class method decorators: @staticmethod, @classmethod, @property — built into Python
  • Class-based decorators: Use __call__ method — useful for stateful decorators

Stacking Decorators

Multiple decorators are applied bottom-up: @a @b @c is equivalent to func = a(b(c(func))). The closest decorator to the function runs first. Order matters — think about whether logging should wrap timing or vice versa.

Common Decorator Patterns

  • Timer/profiler: Measure function execution time
  • Cache/memoize: Store results to avoid recomputation — @lru_cache
  • Auth/permission: Check user permissions before executing
  • Logging: Log function calls, arguments, and return values
  • Retry: Automatically retry failed operations with backoff
  • Rate limiting: Restrict how often a function can be called

Common Pitfall: Forgetting @wraps

Without @wraps(func), the decorated function loses its __name__, __doc__, and __module__. This breaks help(), debugging, and some frameworks (Flask routes depend on function names).

Use Cases

Adding logging, timing, and profiling to functions without modifying their code

Implementing caching/memoization (functools.lru_cache is a built-in decorator)

Access control and authentication in web frameworks (Flask, Django)

Retry logic for flaky network operations and API calls

Input validation, rate limiting, and permission checks

Common Mistakes

Forgetting @wraps(func) — loses function name, docstring, and annotations

Not using `*args`, `**kwargs` in wrapper — breaks decorated functions with different signatures

Confusing decorator order with stacking — @a @b is a(b(func)), not b(a(func))

Forgetting the extra nesting level for parameterized decorators — @retry(3) needs 3 levels

Using decorators when a context manager (with statement) would be more appropriate