Хорошие декораторы Python - PullRequest
       6

Хорошие декораторы Python

3 голосов
/ 11 сентября 2009

Как мне красиво написать декоратор?

В частности проблемы включают в себя: совместимость с другими декораторами, сохранение подписей и т. Д.

Я хотел бы избежать зависимости от модуля декоратора, если это возможно, но если бы было достаточно преимуществ, я бы рассмотрел это.

Относящиеся

Ответы [ 2 ]

6 голосов
/ 11 сентября 2009

Используйте 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'
5 голосов
/ 11 сентября 2009

Написание хорошего декоратора ничем не отличается от написания хорошей функции. Что в идеале означает использование строк документации и обеспечение того, чтобы декоратор был включен в вашу среду тестирования.

Вам обязательно следует использовать либо библиотеку decorator, либо, что лучше, декоратор functools.wraps() в стандартной библиотеке (начиная с 2.5).

Кроме того, лучше всего держать ваши декораторы узко сфокусированными и хорошо продуманными. Не используйте *args или **kw, если ваш декоратор ожидает определенных аргументов. И do укажите, какие аргументы вы ожидаете, вместо:

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
...