Is it possible to replace a function/method decorator at runtime? [ python ]
As Miya mentioned, you can replace the decorator with another function any point before the interpreter gets to that function declaration. However, once the decorator is applied to the function, I don't think there is a way to dynamically replace the decorator with a different one. So for example:
@aDecoratordef myfunc1(): pass# Oops! I didn't want that decorator after all!myfunc1 = bDecorator(myfunc1)
Won't work, because myfunc1 is no longer the function you originally defined; it has already been wrapped. The best approach here is to manually apply the decorators, oldskool-style, i.e:
def myfunc1(): passmyfunc2 = aDecorator(myfunc1)myfunc3 = bDecorator(myfunc1)
Edit: Or, to be a little clearer,
def _tempFunc(): passmyfunc1 = aDecorator(_tempFunc)myfunc1()myfunc1 = bDecorator(_tempFunc)myfunc1()
I don't know if there's a way to "replace" a decorator once it has been applied, but I guess that probably there's not, because the function has already been changed.
You might, anyway, apply a decorator at runtime based on some condition:
#!/usr/bin/env pythonclass 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=Truedef my_decorator(f): if (some_condition): # modify the function return PrintCallInfo(f) else: # leave it as it is return f@my_decoratordef foo(): print "foo"@my_decoratordef bar(s): print "hello",s return s@my_decoratordef foobar(x=1,y=2): print x,y return x + yfoo()bar("world")foobar(y=5)
Here's a terrific recipe to get you started. Basically, the idea is to pass a class instance into the decorator. You can then set attributes on the class instance (make it a Borg if you like) and use that to control the behavior of the decorator itself.
Here's an example:
class Foo: def __init__(self, do_apply): self.do_apply = do_applydef dec(foo): def wrap(f): def func(*args, **kwargs): if foo.do_apply: # Do something! pass return f(*args, **kwargs) return func return wrapfoo = Foo(False)@dec(foo)def bar(x): return xbar('bar') foo.do_apply = True # Decorator now active!bar('baz')
Naturally, you can also incorporate the "decorator decorator" to preserve signatures, etc.