2009-03-13 2 views
14

Если у меня есть функция:Возможно ли заменить декоратор функции/метода во время выполнения? [Питон]


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

Можно ли заменить декоратора во время выполнения?

ответ

2

я не знаю, есть ли способ «заменить» декоратор, как только он был применен, но я думаю, что, вероятно, нет, потому что функция уже была изменена.

Вы могли бы, во всяком случае, применить декоратора во время выполнения на основе некоторых условий:

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

Если декоратор является функцией, просто замените его.

aDecorator = otherDecorator 
+1

Но это не изменит myfunc1(). – 2009-03-13 13:48:58

+0

Я попытался использовать ваш код, функция все еще выполняется с помощью начального декоратора. – Geo

+0

Вау .. не ожидал, что это произойдет. – miya

0

Если вы хотите явно изменить декоратор, вы можете также выбрать более явный подход вместо создания декорированной функции:

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

deco1() и deco2() будет применяться функциональность ваши декораторы предоставляют и вызывают myfunc1() с аргументами.

+0

Да, я мог бы использовать функцию, которая принимает функцию, поскольку это первый параметр, моя функция как второй параметр, а затем аргументы и имеют довольно общий способ сделать это. Мне было интересно, однако, если я могу заменить декоратор с моим собственным.Я хотел бы «играть» с поведением других классов. – Geo

13

Как упоминалось Мией, вы можете заменить декоратор другой функцией любой точки до того, как интерпретатор попадет в это объявление функции. Однако, как только декоратор применяется к функции, я не думаю, что есть способ динамически заменить декоратор на другой. Так, например:

@aDecorator 
def myfunc1(): 
    pass 

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

myfunc1 = bDecorator(myfunc1) 

Не работает, поскольку myfunc1 больше не является функцией, которую вы изначально определили; он уже завернут. Лучший подход здесь должен вручную применить ПОМЕЩЕНИЙ, Oldskool стиле, то есть:

def myfunc1(): 
    pass 

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

Edit: Или, чтобы быть немного понятнее,

def _tempFunc(): 
    pass 

myfunc1 = aDecorator(_tempFunc) 
myfunc1() 
myfunc1 = bDecorator(_tempFunc) 
myfunc1() 
+0

+1: aDecorate (func1) и bDecorate (func1) –

3

Вот потрясающий recipe to get you started. В принципе, идея состоит в том, чтобы передать экземпляр класса в декоратор. Затем вы можете установить атрибуты в экземпляре класса (сделайте его Borg, если хотите), и используйте его для управления поведением самого декоратора.

Вот пример:

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') 

Естественно, вы также можете включить «декоратор декоратор», чтобы сохранить подписи и т.д.

1

Sure - вы можете получить объект-функцию и делать все, что вы хотите с ним:

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

Это предполагает контроль над начальным декоратором. Представьте себе декоратор 'def aDecorator (f): верните лямбда * args, ** kwargs:« HAHAHAHAHAHAHAHA ». В общем, это не близко к действительному подходу. Если у вас __do__ есть контроль над первоначальным декоратором, тогда есть более чистые, более идиоматические способы сделать это. – aaronasterling

0

I знаю, что это старая нить, но мне было интересно это делать.

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] 
Смежные вопросы