Вопрос
Как мне красиво написать декоратор?
В частности, вопросы включают:совместимость с другими декораторами, сохранение подписей и т.д.
Хотелось бы по возможности избежать зависимости от модуля декоратора, но если бы было достаточно преимуществ, то я бы рассмотрел это.
Связанный
- Сохранение сигнатур декорированных функций - гораздо более конкретный вопрос.Ответ здесь — использовать сторонний модуль декоратора, аннотирующий декоратор с помощью @decorator.decorator.
Решение
Написание хорошего декоратора ничем не отличается от написания хорошей функции.В идеале это означает использование строк документации и включение декоратора в вашу среду тестирования.
Вам обязательно следует использовать либо decorator
библиотека или, лучше, functools.wraps()
декоратор в стандартной библиотеке (начиная с версии 2.5).
Помимо этого, лучше всего, чтобы ваши декораторы были узконаправленными и хорошо спроектированными.Не используйте *args
или **kw
если ваш декоратор ожидает конкретных аргументов.И делать укажите ожидаемые аргументы, поэтому вместо:
def keep_none(func):
def _exec(*args, **kw):
return None if args[0] is None else func(*args, **kw)
return _exec
...использовать ...
def keep_none(func):
"""Wraps a function which expects a value as the first argument, and
ensures the function won't get called with *None*. If it is, this
will return *None*.
>>> def f(x):
... return x + 5
>>> f(1)
6
>>> f(None) is None
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
>>> f = keep_none(f)
>>> f(1)
6
>>> f(None) is None
True"""
@wraps(func)
def _exec(value, *args, **kw):
return None if value is None else func(value, *args, **kw)
return _exec
Другие советы
Используйте functools, чтобы сохранить имя и документ.Подпись не сохранится.
Прямо из док.
>>> from functools import wraps
>>> def my_decorator(f):
... @wraps(f)
... def wrapper(*args, **kwds):
... print 'Calling decorated function'
... return f(*args, **kwds)
... return wrapper
...
>>> @my_decorator
... def example():
... """Docstring"""
... print 'Called example function'
...
>>> example()
Calling decorated function
Called example function
>>> example.__name__
'example'
>>> example.__doc__
'Docstring'