Python Decorators: Wrapping & Modifying Functions

Learn Python decorators to modify function behavior dynamically. Understand wrapper functions, @ syntax, and handling arguments in decorators.

Try Python Decorators Code

Overview

Decorators are one of Python's most elegant and powerful design patterns. They allow developers to modify or extend the behavior of a function, method, or class without permanently changing its source code. Think of a decorator as wrapping a function inside another function to execute pre- and post-processing steps. This makes decorators perfect for implementing cross-cutting concerns like logging, timing, validation, or access control.

At their core, decorators work because Python functions are first-class citizens. This means functions can be passed as arguments to other functions, returned from functions, and assigned to variables. A decorator is simply a function that takes another function as an argument, defines a nested wrapper function that executes the original function along with some extra logic, and returns this wrapper function to replace the original.

To make applying decorators simple and clean, Python provides the `@decorator_name` syntactic sugar, placed immediately above the target function definition. When calling the decorated function, Python instead invokes the wrapper. If the function accepts parameters, the wrapper must accept arbitrary arguments (`*args` and `**kwargs`) and forward them properly. Understanding decorators enables you to write highly modular, clean, and DRY (Don't Repeat Yourself) code.

Code Example

A custom decorator that tracks and logs function execution details.

decorators.py
Try in Editor
def call_tracker(func):
    """Decorator that tracks and logs function execution details."""
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args: {args}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned: {result}")
        return result
    return wrapper

@call_tracker
def multiply(a, b):
    return a * b

# Calling the decorated function
result = multiply(5, 4)
Terminal Output
Calling multiply with args: (5, 4)
multiply returned: 20

Real-world Use Cases

  • Logging runtime metrics to performance monitors
  • Enforcing authentication and authorization checks on routes
  • Caching or memoizing expensive computational results

Frequently Asked Questions

How do decorators handle arguments?

By using *args and **kwargs in the nested wrapper function, which allows it to receive and forward any positional or keyword parameters to the wrapped function.

Can you apply multiple decorators to one function?

Yes, you can stack them. They will be executed in order from the top down (innermost to outermost wrap).

Keep Learning

Recommended Python Resources

Expand your knowledge with related interactive tutorials, cheat sheets, and code comparisons.