views:

809

answers:

5

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?

A: 

If the decorator is a function, just replace it.

aDecorator = otherDecorator
miya
But this will not change myfunc1().
unbeknown
I tried using your code, the function still executes with the initial decorator.
Geo
wow.. didn't expect this to happen.
miya
A: 

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.

unbeknown
Yes, 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
Geo
+7  A: 

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()
DNS
+1: aDecorate( func1 ) and bDecorate( func1 )
S.Lott
+2  A: 

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.

zweiterlinde
+1  A: 

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)
Paolo Tedesco