← Назад к вопросам

Как объявляется декоратор

1.0 Junior🔥 151 комментариев
#Автоматизация тестирования

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как объявляется декоратор в 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 вернул ошибку"

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