2012-05-23 9 views
15

¿Es posible decorar una función condicionalmente? Por ejemplo, quiero decorar la función foo() con una función de temporizador (timeit) solo doing_performance_analysis es True (consulte el siguiente código psuedo).cómo hacer un decorador condicional en python 2.6

if doing_performance_analysis: 
    @timeit 
    def foo(): 
    """ 
    do something, timeit function will return the time it takes 
    """ 
    time.sleep(2) 
else: 
    def foo(): 
    time.sleep(2) 

Respuesta

20

decoradores son simplemente callables que devuelven un reemplazo, en caso dado la misma función, una envoltura, o algo completamente diferente. Como tal, se podría crear un decorador condicional:

class conditional_decorator(object): 
    def __init__(self, dec, condition): 
     self.decorator = dec 
     self.condition = condition 

    def __call__(self, func): 
     if not self.condition: 
      # Return the function unchanged, not decorated. 
      return func 
     return self.decorator(func) 

ya se puede utilizar de esta manera:

@conditional_decorator(timeit, doing_performance_analysis) 
def foo(): 
    time.sleep(2) 
+0

Gracias! La sección de comentarios no formatea, así que agregué el código de muestra a su respuesta original. ¿Pueden explicar por qué no se llama a la función de tiempo? – cfpete

+0

El decorador se aplica en el momento de la importación, por lo que las variables de instancia no se están consultando en ese momento. Tendría que escribir un decorador diferente para eso, uno que se inspecciona a sí mismo cuando lo llama. Fuera del alcance para este formato de Q y comentario. :-) –

+0

¿Cómo lo usaría si quisiera usar esto en métodos basados ​​en clases, es decir, vistas basadas en Django Class? Por allí tenemos que usar 'method_decorator'. ¿Cómo hacer que este código sea compatible con eso? – PythonEnthusiast

4

¿Qué tal:

def foo(): 
    ... 

if doing_performance_analysis: 
    foo = timeit(foo) 

Imagino que incluso podría terminar con esto en un decorador que tomaría una bandera booleana y otro decorador, y sólo se aplicaría este último si el indicador se establece en True :

def cond_decorator(flag, dec): 
    def decorate(fn): 
     return dec(fn) if flag else fn 
    return decorate 

@cond_decorator(doing_performance_analysis, timeit) 
def foo(): 
    ... 
9

Un decorador es simplemente una función que se aplica a otra función. Se puede aplicar manualmente:

def foo(): 
    # whatever 
    time.sleep(2) 

if doing_performance_analysis: 
    foo = timeit(foo) 
0

respuesta de Blckknght es grande si usted quiere hacer el registro de entrada cada vez que se llama a la función, pero si tiene una configuración que puede leer una vez y nunca cambia, es posible que no desee verificar la configuración cada vez que se llame a la función decorada. En algunos de nuestros daemons de alto rendimiento en el trabajo, he escrito un decorador que verifica un archivo de configuración una vez cuando el archivo python se carga por primera vez y decide si debe envolverlo o no.

Aquí es una muestra

def timed(f): 
    def wrapper(*args, **kwargs): 
     start = datetime.datetime.utcnow() 
     return_value = f(*args, **kwargs) 
     end = datetime.datetime.utcnow() 
     duration = end - start 

     log_function_call(module=f.__module__, function=f.__name__, start=__start__, end=__end__, duration=duration.total_seconds()) 
    if config.get('RUN_TIMED_FUNCTIONS'): 
     return wrapper 
    return f 

Suponiendo que log_function_call registra su llamada a una base de datos, archivo de registro, o lo que sea y que config.get ('') RUN_TIMED_FUNCTIONS comprueba la configuración global, a continuación, añadir el decorador @timed a una función comprobará una vez en la carga para ver si está cronometrando en este servidor, entorno, etc. y si no, entonces no cambiará la ejecución de la función en la producción o en otros entornos en los que se preocupe por el rendimiento.

0
use_decorator = False 

class myDecorator(object): 
    def __init__(self, f): 
      self.f = f 

    def __call__(self): 
      print "Decorated running..." 
      print "Entering", self.f.__name__ 
      self.f() 
      print "Exited", self.f.__name__ 


def null(a): 
    return a 


if use_decorator == False : 
    myDecorator = null 


@myDecorator 
def CoreFunction(): 
    print "Core Function running" 

CoreFunction()