Pregunta

¿Cómo escribo bien un decorador?

En temas particulares incluyen: compatibilidad con otros decoradores, conservación de firmas, etc.

Me gustaría evitar la dependencia del módulo decorador si es posible, pero si hubiera suficientes ventajas, entonces lo consideraría.

Relacionados

¿Fue útil?

Solución

Escribir un buen decorador no es diferente a escribir una buena función. Lo que significa, idealmente, usar cadenas de documentación y asegurarse de que el decorador esté incluido en su marco de prueba.

Definitivamente, debe usar la biblioteca decorator o, mejor, el functools.wraps () en la biblioteca estándar (desde la versión 2.5).

Más allá de eso, es mejor mantener a sus decoradores concentrados y bien diseñados. No utilice * args o ** kw si su decorador espera argumentos específicos. Y do complete los argumentos que espera, así que en lugar de:

def keep_none(func):
    def _exec(*args, **kw):
        return None if args[0] is None else func(*args, **kw)

    return _exec

... usar ...

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

Otros consejos

Utilice functools para conservar el nombre y el documento. La firma no se conservará.

Directamente desde el doc .

>>> 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'
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top