Ответ
Да, декораторы в Python могут принимать аргументы. Для этого используется дополнительный уровень вложенности функций. Такой декоратор часто называют "декоратором с параметрами".
Структура декоратора с аргументами:
- Внешняя функция принимает аргументы декоратора.
- Она возвращает функцию-декоратор (обычно называемую
decorator). - Эта функция-декоратор принимает целевую функцию (
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 с аргументами. Ну, знаешь, такая штука, когда тебе мало просто обернуть функцию — тебе ещё и параметры туда запихнуть надо. Это как если бы ты не просто сказал «сходи в магаз», а добавил «и купи водки, но не больше двух бутылок, и смотри, чтобы не палёная была». Вот это уровень.
Так вот, чтобы это провернуть, нужно сделать декоратор с параметрами. А для этого, ёпта, добавляется ещё один слой вложенности функций. Получается такая матрёшка, блядь, овердохуища.
Как эта штука устроена:
- Внешняя функция — она как раз и жрёт твои аргументы (типа
max_attempts=3). - Она возвращает сам декоратор (обычно его
decoratorназывают). - А этот самый декоратор уже принимает целевую функцию (
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, ровно так же, как если бы он был самым обычным, без параметров. Вот такая, блядь, хитрая жопа, но когда врубаешься — гениально просто.