2009-11-23 13 views
40

Tengo un problema al usar docstrings con decoradores. Teniendo en cuenta el siguiente ejemplo:Decodificador de Python que maneja las cadenas de documentos

def decorator(f): 
    def _decorator(): 
     print 'decorator active' 
     f() 
    return _decorator 

@decorator 
def foo(): 
    '''the magic foo function''' 
    print 'this is function foo' 

help(foo) 

Ahora la ayuda no me muestra la cadena de documentación de foo como se esperaba, se nota:

Help on function _decorator in module __main__: 

_decorator() 

Sin el decorador, la ayuda es correcta:

Help on function foo in module __main__: 

foo() 
    the magic foo function 

Sé que la función foo está envuelta por el decorador, por lo que el objeto de función ya no es la función foo. Pero, ¿cuál es una buena solución para obtener el docstring (y la ayuda) como se esperaba?

Respuesta

62

Uso functools.wraps() para actualizar los atributos del decorador:

from functools import wraps 

def decorator(f): 
    @wraps(f) 
    def _decorator(): 
     print 'decorator active' 
     f() 
    return _decorator 

@decorator 
def foo(): 
    '''the magic foo function''' 
    print 'this is function foo' 

help(foo) 

Véase también la Standard Library documentation para functools.

+0

Esto no funciona si 'foo' toma cualquier argumento: se reemplazan por lo que usa el' _decorator'. Este es un problema especialmente cuando quieres que tu decorador tome '* args, ** kwds'. Nunca he sido capaz de encontrar la forma correcta de hacer que el docstring sea correcto usando 'functools.wraps'. –

+3

@Scott Griffiths: la docstring seguirá siendo correcta incluso si 'foo' toma argumentos. Sin embargo, 'help (foo)' mostrará la lista de parámetros del '_decorator', ya que reemplaza la función' foo'. No hay una buena forma de evitar esto si está escribiendo decoradores que toman argumentos arbitrarios usando '* args, ** kwargs', pero para mí el punto importante es que la carpeta de documentos se mantiene intacta. Los detalles de los parámetros siempre se pueden especificar en la docstring para mayor claridad. –

+0

Gracias por la información adicional. Recientemente he estado fallando en obtener la descripción de la ayuda correcta para las funciones decoradas, parece un estado bastante pobre, pero entiendo la dificultad ya que la función decorada podría tener una firma completamente diferente. Aún así, debe haber una manera ... :) –

12

he encontrado una solución, pero no saben si es realmente agradable:

def decorator(f): 
    def _decorator(): 
     print 'decorator active' 
     f() 
    _decorator.__name__=f.__name__ 
    _decorator.__doc__=f.__doc__ 
    return _decorator 

La parte con _decorator.__name__=f.__name__ parece un poco horrible ... ¿Qué opinas?

+4

De hecho, este es (casi?) Hacen exactamente lo functools.wraps :) – thomas

+2

No se ve horrible para mí. Dice exactamente lo que quieres que diga. "Quiero que el nombre de esta función sea 'myfunction' en lugar de '_decorator'". – jcdyer

-5

Es probablemente un poco viejo ahora, pero esta es la forma de hacerlo. Sólo asegúrese de que @decorator tiene sangría en la misma línea que def decorator(f):

from functools import wraps 

def decorator(f): 
    @wraps(f) 
    def _decorator(): 
     print 'decorator active' 
     return f() 
    return _decorator 

@decorator 
def foo(): 
    '''the magic foo function''' 
    print 'this is function foo' 

help(foo) 
+8

Esto solo hace eco de la respuesta ya aceptada. –

Cuestiones relacionadas