Продвинутые декораторы
Параметризованные декораторы, декораторы классов, наложение и инструментарий 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 повторяется три раза.