Декораторы в Python — удобный инструмент. Если ими пользоваться с умом, они значительно повышают читаемость кода и его повторное использование.
Но есть один момент, который меня немного напрягает: для декоратора с параметрами по умолчанию нужно ставить пустые круглые скобки после его вызова. Время от времени это приводит к ошибкам. Особенно это актуально, если декоратор является частью публичного API.
@decorator_wo_params
def f1():
pass
@decorator_w_defaults_params() # Ну зачем здесь пустые скобки?
def f2():
pass
С этим можно смириться. В режиме жёстких сроков и большого потока задач времени на поиск решения, написание и тестирование дополнительного кода может просто не быть. Нормальное тестирование выявит проблему моментально. Но иногда невозможно удержаться и не сделать свой код немного красивее.
Предположим, что требуется декоратор, который увеличивает значение функции в заданное количество раз. По умолчанию — в 10 раз. У меня получился следующий код:
from functools import wraps
def multiplicator(method=None, rate=10):
def decorator(method):
@wraps(method)
def wrapper(*args, **kwargs):
return rate * method(*args, **kwargs)
return wrapper
if callable(method):
return decorator(method)
return decorator
В основе лежит идея, предложенная Михаэлем Анджелетти на Stack Overflow (Python — making decorators with optional arguments). Я немного модифицировал его код под поставленную задачу, но идея сохранена.
Если декоратор вызван без скобок, то первым параметром в функцию multiplicator прилетит функция. Если же вызвать со скобками (не важно, с параметрами или нет), то в if мы не попадаем — возвращается декоратор.
Код рабочий, но не без проблем:
- параметры в подобный декоратор нужно передавать по имени, что не всегда удобно;
- непонятный
methodв подсказке IDE будет глаза мозолить.
С другой стороны, код практически не усложнился, читаемость не снизилась.
Вот ещё пара интересных вариантов реализации декораторов с параметрами, на которые можно посмотреть. Возможно, для ваших проектов они окажутся более подходящими: