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

Ответ

Да, декораторы в 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), как если бы он был обычным декоратором без аргументов.

Ответ 18+ 🔞

Э, слушай, смотри, тут про декораторы в Python с аргументами. Ну, знаешь, такая штука, когда тебе мало просто обернуть функцию — тебе ещё и параметры туда запихнуть надо. Это как если бы ты не просто сказал «сходи в магаз», а добавил «и купи водки, но не больше двух бутылок, и смотри, чтобы не палёная была». Вот это уровень.

Так вот, чтобы это провернуть, нужно сделать декоратор с параметрами. А для этого, ёпта, добавляется ещё один слой вложенности функций. Получается такая матрёшка, блядь, овердохуища.

Как эта штука устроена:

  1. Внешняя функция — она как раз и жрёт твои аргументы (типа max_attempts=3).
  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, ровно так же, как если бы он был самым обычным, без параметров. Вот такая, блядь, хитрая жопа, но когда врубаешься — гениально просто.