Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как объявляется декоратор в Python
Декоратор в Python — это паттерн проектирования, который позволяет динамически изменять или расширять поведение функции, метода или класса, не меняя их исходный код. По сути, это функция высшего порядка, которая принимает другую функцию в качестве аргумента и возвращает новую функцию с дополнительной логикой.
Базовое объявление и синтаксис
Синтаксически декоратор применяется с помощью символа @ перед его именем над целевой функцией. Объявить декоратор можно несколькими способами.
1. Декоратор как функция
Самый простой способ — создать функцию-декоратор, которая принимает функцию, возвращает обёрнутую функцию, и использует @decorator_name.
def my_decorator(func):
def wrapper(*args, **kwargs):
print(f"Вызов функции {func.__name__} с аргументами: {args}, {kwargs}")
result = func(*args, **kwargs)
print(f"Функция {func.__name__} завершилась с результатом: {result}")
return result
return wrapper
@my_decorator
def add(a, b):
return a + b
print(add(2, 3)) # Выведет логирование и результат 5
Здесь my_decorator — функция-декоратор. Внутри неё wrapper — обёртка, которая выполняет дополнительный код до и после вызова оригинальной func.
2. Декоратор с параметрами
Если декоратору нужно передавать параметры (например, уровень логирования), требуется дополнительный уровень вложенности.
def repeat(times):
"""Декоратор, повторяющий выполнение функции заданное число раз."""
def decorator(func):
def wrapper(*args, **kwargs):
total_result = None
for i in range(times):
print(f"Повторение {i + 1}/{times}")
total_result = func(*args, **kwargs)
return total_result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Привет, {name}!")
return f"Сообщение для {name}"
greet("Алиса")
В этом случае repeat(times=3) возвращает декоратор decorator, который уже оборачивает функцию greet.
3. Декоратор для класса
Декоратор можно применять и к классам — он будет воздействовать на конструктор или весь класс.
def singleton(cls):
"""Декоратор класса, превращающий его в Singleton."""
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Config:
def __init__(self):
self.settings = {}
config1 = Config()
config2 = Config()
print(config1 is config2) # True, это один и тот же экземпляр
Ключевые аспекты объявления декораторов
-
Сохранение метаданных: При использовании простых обёрток теряются метаданные оригинальной функции (имя, документация). Для их сохранения используют
functools.wraps.from functools import wraps def logged(func): @wraps(func) # Сохраняет имя, документацию и другие атрибуты func def wrapper(*args, **kwargs): print(f"LOG: Вызов {func.__name__}") return func(*args, **kwargs) return wrapper @logged def calculate(x): """Возвращает квадрат числа.""" return x * x print(calculate.__name__) # 'calculate', а не 'wrapper' print(calculate.__doc__) # 'Возвращает квадрат числа.' -
Цепочка декораторов: К функции можно применять несколько декораторов. Они выполняются снизу вверх (ближайший к функции — первый).
@decorator1 @decorator2 def my_func(): pass # Эквивалентно: decorator1(decorator2(my_func)) -
Декораторы методов: Принцип объявления одинаков для методов класса, но нужно учитывать
selfкак первый аргумент.
Практическое применение в тестировании (QA)
В контексте QA и автоматизации тестирования декораторы часто используются для:
- Логирования действий тестовых функций.
- Измерения времени выполнения тестов.
- Повторного запуска упавших тестов (retry logic).
- Проверки предусловий (например, доступность среды).
- Управления тестовыми данными (setup/teardown в стиле unittest).
Например, декоратор для повторного выполнения теста при неудаче:
import time
from functools import wraps
def retry(max_attempts=3, delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
attempts = 0
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except AssertionError as e:
attempts += 1
print(f"Попытка {attempts}/{max_attempts} не удалась: {e}")
if attempts < max_attempts:
time.sleep(delay)
raise # Если все попытки исчерпаны
return wrapper
return decorator
@retry(max_attempts=3, delay=2)
def test_flaky_api():
# Симуляция ненадёжного теста
import random
assert random.choice([True, False]), "API вернул ошибку"
Таким образом, объявление декоратора — это создание функции, которая принимает и возвращает функцию, с возможностью добавления параметров и сохранения метаданных. Этот механизм значительно повышает переиспользуемость и поддерживаемость кода, что особенно ценно в разработке тестового фреймворка.