Перейти к содержанию

Начало урока

Продвинутые декораторы

Параметризованные декораторы, декораторы классов, наложение и инструментарий functools

Главная идея: Вы уже знаете, что декоратор - это вызываемый объект, который принимает функцию и возвращает замену. На продвинутом уровне декоратор по сути является небольшим хуком времени определения: он выполняется один раз, когда исполняется оператор def или class, и может переписать идентичность объекта, добавить состояние, где-то зарегистрировать его или принять собственную конфигурацию. Стандартная библиотека поставляет промышленного качества декораторы в модуле functools, к которым стоит обращаться прежде, чем писать свои.
К концу этой темы вы сможете:
  • писать параметризованный декоратор (фабрику декораторов) и объяснять три уровня его вызова,
  • создавать декоратор, работающий с аргументами и без них,
  • различать два значения понятия декоратор класса и реализовывать оба,
  • точно рассуждать о порядке применения наложенных декораторов и их запуска,
  • применять functools.wraps, lru_cache/cache и singledispatch и называть их реальные компромиссы.

1. Параметризованные декораторы (фабрики декораторов)

Обычный декоратор принимает ровно один аргумент: декорируемую функцию. Как же тогда @lru_cache(maxsize=256) принимает аргументы? Секрет в одном дополнительном уровне. @repeat(times=3) - это не декоратор, а функция, которая возвращает декоратор. Python сначала вычисляет repeat(times=3), и то, что он возвращает, затем используется как декоратор. Это даёт параметризованному декоратору три вложенных уровня.

import functools

def repeat(times):                       # уровень 1: фабрика, принимает настройку
    def decorator(func):                 # уровень 2: сам декоратор, принимает функцию
        @functools.wraps(func)
        def wrapper(*args, **kwargs):    # уровень 3: обёртка, принимает аргументы вызова
            result = None
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(times=3)
def ping():
    print("ping")

Читайте @repeat(times=3) как два шага: d = repeat(times=3) возвращает decorator, затем ping = d(ping). Внешний times захватывается замыканием, поэтому каждый вызов декорированного ping повторяется три раза.

Продолжите этот урок на YourAIZone

В полном уроке — объяснение целиком, разобранные примеры, частые ошибки, тесты с мгновенной проверкой и ИИ-репетитор. Начните сразу как гость или создайте аккаунт, чтобы сохранять прогресс.

В полном уроке

  • Объяснение целиком
  • Разобранные примеры
  • Частые ошибки и как их избежать
  • Тестовые вопросы: 15
  • ИИ-репетитор, который отвечает на вопросы по теме
  • Отслеживание прогресса для ученика, родителей и учителя