Pergunta

Se eu tiver uma função:


@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()

É possível substituir um decorador em tempo de execução?

Foi útil?

Solução

Eu não sei se há uma maneira de "substituir" um decorador uma vez que foi aplicado, mas acho que, provavelmente, não há, porque a função já foi alterado.

Você pode, de qualquer forma, aplicar um decorador em tempo de execução com base em alguma condição:

#!/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)

Outras dicas

Como Miya mencionado, você pode substituir o decorador com outra função qualquer ponto antes de o intérprete começa a essa declaração função. No entanto, uma vez que o decorador é aplicado para a função, eu não acho que há uma maneira para substituir dinamicamente o decorador com um diferente. Assim, por exemplo:

@aDecorator
def myfunc1():
    pass

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

myfunc1 = bDecorator(myfunc1)

não vai funcionar, porque MyFunc1 não é mais a função que originalmente definido; já foi embrulhado. A melhor abordagem aqui é para aplicar manualmente os decoradores, oldskool-style, ou seja:

def myfunc1():
    pass

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

Edit: Ou, para ser um pouco mais claro,

def _tempFunc():
    pass

myfunc1 = aDecorator(_tempFunc)
myfunc1()
myfunc1 = bDecorator(_tempFunc)
myfunc1()

Aqui está uma excelente receita para você começar . Basicamente, a idéia é passar uma instância de classe para o decorador. Você pode então definir atributos na instância de classe (torná-lo um Borg se quiser) e usar isso para controlar o comportamento do próprio decorador.

Aqui está um exemplo:

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, você pode também incorporar a "decorador decorador" para preservar assinaturas, etc.

Claro - você pode obter o objeto de função e fazer o que quiser com ele:

# 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__()

Se você quiser alterar explicitamente o decorador, assim como você pode escolher uma abordagem mais explícita em vez de criar uma função decorado:

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

deco1 () e Deco2 () seria aplicável a funcionalidade seus decoradores fornecer e MyFunc1 call () com os argumentos.

Eu sei que é uma discussão antiga, mas eu me diverti fazendo isso

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]

Se o decorador é uma função, simplesmente substituí-lo.

aDecorator = otherDecorator
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top