На этом шаге мы рассмотрим решение этой задачи.
Вы хотите создать функцию-декоратор, которая принимала бы аргументы.
Давайте покажем процесс приема аргументов на примере. Предположим, вы хотите написать декоратор, который добавляет к функции логирование и при этом позволяет пользователю указать уровень логирования и прочие параметры через аргументы. Вот как вы можете это сделать:
>>> from functools import wraps >>> import logging >>> def logged(level, name=None, message=None): ''' Добавляет логирование в функцию. level - это уровень логирования, name - это название логгера, message - это сообщение в лог. Если name и message не определены, они будут дефолтными от имени функции и ее модуля. ''' def decorate(func): logname = name if name else func.__module__ log = logging.getLogger(logname) logmsg = message if message else func.__name__ @wraps(func) def wrapper(*args, **kwargs): log.log(level, logmsg) return func(*args, **kwargs) return wrapper return decorate >>> # Пример использования >>> @logged(logging.DEBUG) def add(x, y): return x + y >>> @logged(logging.CRITICAL, 'example') def spam(): print('Spam!') >>> >>> logging.basicConfig(level=logging.DEBUG) >>> print(add(2, 3)) DEBUG:__main__:add 5 >>> spam() CRITICAL:example:spam Spam! >>>
На первый взгляд реализация выглядит нетривиальной, но идея относительно проста. Самая внешняя функция logged() принимает желаемые аргументы
и просто делает их доступными внутренним функциям декоратора. Внутренняя функция decorate() принимает функцию и помещает ее в обертку
обычным образом. Ключевой момент в том, что обертка может использовать аргументы, переданные в logged().
Создавать декораторы, принимающие аргументы, довольно сложно, поскольку они используют "подкапотную" последовательность вызовов. Говоря конкретно, если у вас есть вот такой код:
@decorator(x, y, z) def func(a, b): pass
def func(a, b): pass func = decorator(x, y, z)(func)
Обратите внимание, что результат вызова decorator(x, y, z) должен быть вызываемым объектом, который, в свою очередь, принимает функцию и оборачивает ее.
На следующем шаге мы рассмотрим определение декоратора с настраиваемыми пользователем атрибутами.