Можно ли передавать аргументы в декоратор в Python?

«Можно ли передавать аргументы в декоратор в Python?» — вопрос из категории Python, который задают на 33% собеседований Data Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, декораторы в Python могут принимать аргументы. Для этого используется дополнительный уровень вложенности функций. Такой декоратор часто называют "декоратором с параметрами".

Структура декоратора с аргументами:

  1. Внешняя функция принимает аргументы декоратора.
  2. Она возвращает функцию-декоратор (обычно называемую decorator).
  3. Эта функция-декоратор принимает целевую функцию (func) и возвращает функцию-обёртку (wrapper).

Пример: декоратор для повторного выполнения функции с заданным количеством попыток.

import time
from functools import wraps

def retry(max_attempts=3, delay=1):
    """Декоратор для повторного вызова функции при возникновении исключения."""
    def decorator(func):
        @wraps(func)  # Сохраняем метаданные оригинальной функции
        def wrapper(*args, **kwargs):
            last_exception = None
            for attempt in range(1, max_attempts + 1):
                try:
                    print(f"Попытка {attempt} из {max_attempts}")
                    return func(*args, **kwargs)
                except Exception as e:
                    last_exception = e
                    if attempt < max_attempts:
                        time.sleep(delay)
            # Если все попытки исчерпаны
            raise last_exception
        return wrapper
    return decorator

# Использование с аргументами по умолчанию
@retry()
def connect_to_service():
    # ... логика подключения
    pass

# Использование с кастомными аргументами
@retry(max_attempts=5, delay=2)
def fetch_data_from_api(url):
    # ... логика запроса
    pass

Ключевой момент: Когда вы пишете @retry(max_attempts=5), сначала вызывается retry(max_attempts=5), который возвращает сам декоратор (decorator). Затем этот декоратор применяется к функции (fetch_data_from_api), как если бы он был обычным декоратором без аргументов.