Agradável Python decoradores
Pergunta
Como posso muito bem escrever um decorador?
Em questões específicas incluem: a compatibilidade com outros decoradores, conservação de assinaturas, etc
.Eu gostaria de evitar dependência no módulo decorador, se possível, mas se havia vantagens suficientes, então eu consideraria isso.
Relacionados
- Preservando assinaturas de funções decorados - muito pergunta mais específica. A resposta aqui é usar o módulo decorador de terceiros anotar o decorador com @ decorator.decorator
Solução
Escrever um bom decorador não é diferente, em seguida, escrever uma função boa. O que significa que, idealmente, usando docstrings e certificando-se o decorador está incluído na sua estrutura de teste.
Você deve definitivamente usar a biblioteca decorator
, ou melhor, o decorador functools.wraps()
na biblioteca padrão (desde 2.5).
Além disso, é melhor manter seus decoradores estritamente focalizados e bem desenhados. Não use *args
ou **kw
se o seu decorador espera argumentos específicos. E do preencher o que os argumentos que você espera, então em vez de:
def keep_none(func):
def _exec(*args, **kw):
return None if args[0] is None else func(*args, **kw)
return _exec
... use ...
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
Outras dicas
Use functools para preservar o nome e doc. A assinatura não serão preservadas.
Diretamente do 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'