Is it possible to replace a Python function/method decorator at runtime?
Solution 1
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 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)
Solution 2
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:
@aDecorator
def 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():
pass
myfunc2 = aDecorator(myfunc1)
myfunc3 = bDecorator(myfunc1)
Edit: Or, to be a little clearer,
def _tempFunc():
pass
myfunc1 = aDecorator(_tempFunc)
myfunc1()
myfunc1 = bDecorator(_tempFunc)
myfunc1()
Solution 3
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_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')
Naturally, you can also incorporate the "decorator decorator" to preserve signatures, etc.
Solution 4
If you want to explicitely change the decorator, you might as well choose a more explicit approach instead of creating a decorated function:
deco1(myfunc1, arg1, arg2)
deco2(myfunc1, arg2, arg3)
deco1() and deco2() would apply the functionality your decorators provide and call myfunc1() with the arguments.
Solution 5
Sure - you can get the function object and do whatever you want with it:
# 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__()
![MarkD](https://i.stack.imgur.com/yQ5nj.jpg?s=256&g=1)
MarkD
Updated on June 03, 2022Comments
-
MarkD about 2 years
If I have a function :
@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()
Is it possible to replace a decorator at runtime?
-
Admin over 15 yearsBut this will not change myfunc1().
-
MarkD over 15 yearsI tried using your code, the function still executes with the initial decorator.
-
MarkD over 15 yearsYes, I could have used a function that takes a function as it's first param, my function as it's second param, followed by the arguments, and have a pretty generic way of doing it.I was interested however, if I can replace a decorator with my own.I would like to "play" with other classes's behaviour
-
aaronasterling over 13 yearsThis assumes control over the initial decorator. Imagine the decorator
def aDecorator(f): return lambda *args, **kwargs: "HAHAHAHAHAHAHA"
. In general, this isn't close to a valid approach. If you do happen to have control of the initial decorator, then there are cleaner, more idiomatic ways to do this.