是否可以在运行时替换函数/方法装饰器?[ Python ]

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/642762/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 20:31:53  来源:igfitidea点击:

Is it possible to replace a function/method decorator at runtime? [ python ]

pythonruntimelanguage-featuresdecorator

提问by Geo

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?

是否可以在运行时替换装饰器?

采纳答案by Paolo Tedesco

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)

回答by DNS

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:

正如 Miya 所提到的,您可以在解释器到达该函数声明之前的任何时候用另一个函数替换装饰器。但是,一旦将装饰器应用于函数,我认为没有办法用不同的装饰器动态替换装饰器。例如:

@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:

不起作用,因为 myfunc1 不再是您最初定义的函数;它已经被包裹了。这里最好的方法是手动应用装饰器,oldskool 风格,即:

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

回答by zweiterlinde

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.

这是一个很棒的食谱,可以帮助您入门。基本上,这个想法是将一个类实例传递给装饰器。然后,您可以在类实例上设置属性(如果您愿意,可以将其设为 Borg)并使用它来控制装饰器本身的行为。

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.

当然,您还可以合并“装饰器装饰器”以保留签名等。

回答by Freddie

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

回答by Freddie

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.

deco1() 和 deco2() 将应用您的装饰器提供的功能并使用参数调用 myfunc1()。

回答by Freddie

I know it's an old thread, but I had fun doing this

我知道这是一个旧线程,但我很高兴这样做

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]

回答by miya

If the decorator is a function, just replace it.

如果装饰器是一个函数,只需替换它。

aDecorator = otherDecorator