2009-03-13 9 views
14

Si tengo una función:¿Es posible reemplazar un decorador de función/método en tiempo de ejecución? [Python]


@aDecorator 
def myfunc1(): 
    # do something here 

if __name__ = "__main__": 
    # this will call the function and will use the decorator @aDecorator 
    myfunc1() 
    # now I want the @aDecorator to be replaced with the decorator @otherDecorator 
    # so that when this code executes, the function no longer goes through 
    # @aDecorator, but instead through @otherDecorator. How can I do this? 
    myfunc1() 

¿Es posible sustituir un decorador en tiempo de ejecución?

Respuesta

2

No sé si hay una manera de "reemplazar" una decorador una vez que se ha aplicado, pero supongo que probablemente no, porque la función ya se ha cambiado.

Es posible que, de todos modos, aplicar un decorador en tiempo de ejecución en base a alguna condición:

#!/usr/bin/env python 

class PrintCallInfo: 
    def __init__(self,f): 
     self.f = f 
    def __call__(self,*args,**kwargs): 
     print "-->",self.f.__name__,args,kwargs 
     r = self.f(*args,**kwargs) 
     print "<--",self.f.__name__,"returned: ",r 
     return r 

# the condition to modify the function... 
some_condition=True 

def my_decorator(f): 
    if (some_condition): # modify the function 
     return PrintCallInfo(f) 
    else: # leave it as it is 
     return f 

@my_decorator 
def foo(): 
    print "foo" 

@my_decorator 
def bar(s): 
    print "hello",s 
    return s 

@my_decorator 
def foobar(x=1,y=2): 
    print x,y 
    return x + y 

foo() 
bar("world") 
foobar(y=5) 
-1

Si el decorador es una función, simplemente reemplazarlo.

aDecorator = otherDecorator 
+1

pero esto no va a cambiar MyFunc1(). –

+0

Intenté usar su código, la función todavía se ejecuta con el decorador inicial. – Geo

+0

wow ... no esperaba que esto sucediera. – miya

0

Si desea cambiar explícitamente el decorador, que también podría optar por un enfoque más explícito en lugar de crear una función decorada:

deco1(myfunc1, arg1, arg2) 
deco2(myfunc1, arg2, arg3) 

deco1() y deco2() se aplicaría la funcionalidad sus decoradores proporcionan y llaman a myfunc1() con los argumentos.

+0

Sí, podría haber utilizado una función que toma una función ya que es primer parámetro, mi función, ya que es el segundo parámetro, seguido de los argumentos, y tiene una forma genérica bonita de hacerlo.Sin embargo, estaba interesado si puedo reemplazar un decorador con el mío. Me gustaría "jugar" con el comportamiento de otras clases – Geo

13

Como se mencionó Miya, puede reemplazar el decorador con otra función de cualquier punto antes de que el intérprete llega a ese declaración de la función. Sin embargo, una vez que se aplica el decorador a la función, no creo que haya una manera de reemplazar dinámicamente el decorador por uno diferente. Por ejemplo:

@aDecorator 
def myfunc1(): 
    pass 

# Oops! I didn't want that decorator after all! 

myfunc1 = bDecorator(myfunc1) 

No funcionará, porque myfunc1 ya no es la función que usted definió originalmente; ya ha sido envuelto. El mejor enfoque aquí es aplicar manualmente el decoradores, oldskool de estilo, es decir:

def myfunc1(): 
    pass 

myfunc2 = aDecorator(myfunc1) 
myfunc3 = bDecorator(myfunc1) 

Editar: O, para ser un poco más claro,

def _tempFunc(): 
    pass 

myfunc1 = aDecorator(_tempFunc) 
myfunc1() 
myfunc1 = bDecorator(_tempFunc) 
myfunc1() 
+0

+1: aDecorate (func1) y bDecorate (func1) –

3

He aquí una excelente recipe to get you started. Básicamente, la idea es pasar una instancia de clase al decorador. A continuación, puede establecer atributos en la instancia de la clase (convertirlo en Borg si lo desea) y usar eso para controlar el comportamiento del decorador.

He aquí un ejemplo:

class Foo: 
    def __init__(self, do_apply): 
     self.do_apply = do_apply 

def dec(foo): 
    def wrap(f): 
     def func(*args, **kwargs): 
      if foo.do_apply: 
       # Do something! 
       pass 
      return f(*args, **kwargs) 
     return func 
    return wrap 

foo = Foo(False) 
@dec(foo) 
def bar(x): 
    return x 

bar('bar') 
foo.do_apply = True 
# Decorator now active! 
bar('baz') 

Naturalmente, también se puede incorporar el "decorador decorador" para preservar firmas, etc.

1

Claro - se puede obtener el objeto de función y hacer lo que quiera con él:

# Bypass a decorator 

import types 

class decorator_test(object): 

    def __init__(self, f): 
     self.f = f 

    def __call__(self): 
     print "In decorator ... entering: ", self.f.__name__ 
     self.f() 
     print "In decorator ... exiting: ", self.f.__name__ 


@decorator_test 
def func1(): 
    print "inside func1()" 

print "\nCalling func1 with decorator..." 
func1() 

print "\nBypassing decorator..." 
for value in func1.__dict__.values(): 
    if isinstance(value, types.FunctionType) and value.func_name == "func1": 
     value.__call__() 
+0

Esto asume el control sobre el decorador inicial. Imagine el decorador 'def aDecorator (f): return lambda * args, ** kwargs:" HAHAHAHAHAHAHA "'. En general, esto no es cercano a un enfoque válido. Si tiene control del decorador inicial, hay formas más limpias e idiomáticas de hacerlo. – aaronasterling

0

I sé que es un hilo viejo, pero me he divertido haciendo esto

def change_deco(name, deco, placeholder=' #'): 
with open(name + '.py', 'r') as file: 
    lines = file.readlines() 
for idx, string in enumerate(lines): 
    if placeholder in string and repr(placeholder) not in string: 
     lines[idx] = f' @{deco}\r\n' 
exec(''.join(lines)) 
return locals()[name] 
Cuestiones relacionadas