October 13, 2024

Decorators with Parameters in Python

In Python, decorators are a powerful feature that allows you to modify the behavior of functions or methods. A decorator with parameters is an extension of this concept, where you can pass arguments to the decorator itself. This provides greater flexibility in how decorators are applied and how they modify function behavior.

1. Basic Decorators

Before diving into decorators with parameters, let’s review a basic decorator. A decorator is a function that wraps another function to extend its behavior:

# Basic decorator
def simple_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@simple_decorator
def say_hello():
    print("Hello!")

say_hello()

In this example, simple_decorator is applied to the say_hello function. It prints messages before and after calling the original function.

2. Decorators with Parameters

Decorators with parameters are useful when you need to customize the behavior of the decorator. To create a decorator with parameters, you need to define an outer function that takes the parameters and an inner function that acts as the actual decorator:

2.1 Creating a Decorator with Parameters

# Decorator with parameters
def repeat(num_times):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
        return wrapper
    return decorator_repeat

@repeat(num_times=3)
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("Alice")

In this example, the repeat decorator takes an argument num_times, which specifies how many times the decorated function should be executed. The decorator_repeat function wraps the actual function and handles the logic for repeating it.

2.2 Using Multiple Parameters

Decorators can also handle multiple parameters. You simply expand the outer function to accept additional arguments:

# Decorator with multiple parameters
def log(level, prefix):
    def decorator_log(func):
        def wrapper(*args, **kwargs):
            print(f"{prefix} - {level}: Calling function '{func.__name__}' with arguments {args} and kwargs {kwargs}")
            result = func(*args, **kwargs)
            print(f"{prefix} - {level}: Function '{func.__name__}' returned {result}")
            return result
        return wrapper
    return decorator_log

@log(level='INFO', prefix='MyApp')
def add(x, y):
    return x + y

add(5, 3)

In this example, the log decorator takes two parameters: level and prefix. It logs information before and after the function call, including the function name, arguments, and result.

3. Summary

Decorators with parameters provide a flexible way to modify and enhance the behavior of functions or methods. By defining an outer function to accept parameters and an inner function to act as the actual decorator, you can create reusable and customizable decorators. This approach allows for more dynamic and configurable decorators, improving the maintainability and functionality of your code.