Декораторы с аргументами в Python
В этой статье вы узнаете, как использовать декораторы с аргументами и что такое фабрики декораторов.
Предположим, у нас есть функция say(), которая выводит какое-то сообщение:
def say(message):
''' выводит сообщение
Аргументы
message: сообщение, которое нужно вывести
'''
print(message)
и мы хотим выполнять эту функцию 5 раз, каждый раз когда вызываем ее. Например, если мы вызовем функцию с таким аргументом say("Привет"), вывод должен быть таким:
Привет
Привет
Привет
Привет
Привет
Это можно сделать с помощью простого декоратора:
@repeat
def say(message):
''' выводит сообщение
Аргументы
message: сообщение, которое нужно вывести
'''
print(message)
Для этого просто нужно создать этот декоратор. Например, вот так:
def repeat(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
for _ in range(5):
result = fn(*args, **kwargs)
return result
return wrapper
Вот весь код:
from functools import wraps
def repeat(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
for _ in range(5):
result = fn(*args, **kwargs)
return result
return wrapper
@repeat
def say(message):
''' выводит сообщение
Аргументы
message: сообщение, которое нужно вывести
'''
print(message)
Но что если нужно выполнить функцию say() 10 раз? В этом случае придется менять захардкоженное значение 5 в декораторе repeat.
Такое решение — негибкое. Например, если вы захотите использовать декоратор repeat для выполнения функции 5 раз, а потом — 10 раз. Декоратор repeat так сделать не позволит.
Чтобы исправить это, нужно изменить декоратор repeat так, чтобы он принимал аргумент, задающий количество раз, которое должна выполниться функция, следующим образом:
@repeat(5)
def say(message):
...
Чтобы определить декоратор repeat, repeat(5) должен возвращать исходный декоратор.
def repeat(times):
# возвращает оригинальный декоратор repeat
Новая функция repeat возвращает декоратор. Такие функции часто называют фабрикой декораторов.
Следующая функция repeat возвращает декоратор:
def repeat(times):
''' вызывает функцию количество раз, равное times '''
def decorate(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
for _ in range(times):
result = fn(*args, **kwargs)
return result
return wrapper
return decorate
В этом коде функция decorate() является декоратором. Она эквивалентна оригинальному декоратору repeat.
Обратите внимание, что новая функция repeat() не является декоратором. Это фабрика декораторов, которая возвращает декоратор.
Соберем все вместе:
from functools import wraps
def repeat(times):
''' вызывает функцию количество раз, равное times '''
def decorate(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
for _ in range(times):
result = fn(*args, **kwargs)
return result
return wrapper
return decorate
@repeat(10)
def say(message):
''' выводит сообщение
Аргументы
message: сообщение, которое нужно вывести
'''
print(message)
say('Привет')Закрепите практикой
Задачи с автоматической проверкой — решайте прямо здесь, не уходя из учебника.