Можно ли заменить декоратор функции/метода во время выполнения? [python]
Если у меня есть функция:
@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()
Можно ли заменить декоратор во время выполнения?
Ответы
Ответ 1
Я не знаю, есть ли способ "заменить" декоратор после его применения, но я думаю, что, вероятно, нет, потому что функция уже была изменена.
В любом случае вы можете применить декоратор во время выполнения на основе некоторого условия:
#!/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)
Ответ 2
Как упоминал Мийя, вы можете заменить декоратора другой функцией любой точки до того, как интерпретатор попадет в это объявление функции. Однако, как только декоратор применяется к функции, я не думаю, что есть способ динамически заменить декоратор на другой. Так, например:
@aDecorator
def myfunc1():
pass
# Oops! I didn't want that decorator after all!
myfunc1 = bDecorator(myfunc1)
Не будет работать, потому что myfunc1 больше не является функцией, которую вы изначально определили; он уже завернут. Наилучший подход здесь - вручную применить декораторы, стиль oldskool, i.e:
def myfunc1():
pass
myfunc2 = aDecorator(myfunc1)
myfunc3 = bDecorator(myfunc1)
Изменить: Или, чтобы быть немного яснее,
def _tempFunc():
pass
myfunc1 = aDecorator(_tempFunc)
myfunc1()
myfunc1 = bDecorator(_tempFunc)
myfunc1()
Ответ 3
Вот потрясающий рецепт, чтобы вы начали. В принципе, идея состоит в том, чтобы передать экземпляр класса в декоратор. Затем вы можете установить атрибуты в экземпляре класса (сделайте его 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')
Естественно, вы также можете включить "декоратор декоратора" для сохранения подписей и т.д.
Ответ 4
Конечно - вы можете получить объект функции и делать с ним все, что хотите:
# 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__()
Ответ 5
Если вы хотите явно изменить декоратор, вы можете также выбрать более явный подход вместо создания декорированной функции:
deco1(myfunc1, arg1, arg2)
deco2(myfunc1, arg2, arg3)
deco1() и deco2() применили бы функциональность, которую предоставляют ваши декораторы, и вызовите myfunc1() с аргументами.
Ответ 6
Я знаю, что это старый поток, но мне было весело это делать
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]
Ответ 7
Если декоратор является функцией, просто замените его.
aDecorator = otherDecorator