Pregunta

Si tengo una función:


@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 reemplazar un decorador en tiempo de ejecución?

¿Fue útil?

Solución

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

De todos modos, puede aplicar un decorador en tiempo de ejecución en función de 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)

Otros consejos

Como mencionó Miya, puede reemplazar el decorador con otra función en cualquier momento antes de que el intérprete llegue a esa declaración de función. Sin embargo, una vez que el decorador se aplica a la función, no creo que haya una forma de reemplazar dinámicamente el decorador por uno diferente. Entonces, 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 definió originalmente; Ya ha sido envuelto. El mejor enfoque aquí es aplicar manualmente los decoradores, estilo oldskool, 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()

Aquí hay una excelente receta para comenzar . Básicamente, la idea es pasar una instancia de clase al decorador. A continuación, puede establecer atributos en la instancia de clase (que sea un Borg si lo desea) y usar eso para controlar el comportamiento del decorador en sí.

Aquí hay 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 puede incorporar el decorador decorador para preservar firmas, etc.

Claro, 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__()

Si desea cambiar explícitamente el decorador, también podría elegir 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 () aplicarían la funcionalidad que proporcionan sus decoradores y llamarían a myfunc1 () con los argumentos.

Sé que es un hilo viejo, pero me divertí 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]

Si el decorador es una función, simplemente reemplácelo.

aDecorator = otherDecorator
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top