关于python:使用装饰器将变量带入装饰函数名称空间

Using a decorator to bring variables into decorated function namespace

假设我有一个像这样的函数:

1
2
def func(arg1, arg2):
    return arg1 + arg2 + arg3

我想引入arg3,使用传递给装饰师的参数。这有可能吗?如果有,怎么做?

我试过这样做,这里建议我可以把论点传递给装饰师:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from functools import wraps

def addarg(arg):
    def decorate(func):
        arg3 = arg
        @wraps(func)
        def wrapped(*args):
            return func(*args)
        return wrapped
    return decorate

@addarg(3)
def func(arg1, arg2):
    return arg1 + arg2 + arg3

if __name__ =="__main__":
    print(func(1, 2))

但我得到了这个错误(可以理解):

1
2
3
4
5
6
7
8
Traceback (most recent call last):
  File"C:/Users/pbreach/Dropbox/FIDS/PhD/Code/anemi3/utils.py", line 19, in <module>
    print(func(1, 2))
  File"C:/Users/pbreach/Dropbox/FIDS/PhD/Code/anemi3/utils.py", line 9, in wrapped
    return func(*args)
  File"C:/Users/pbreach/Dropbox/FIDS/PhD/Code/anemi3/utils.py", line 16, in func
    return arg1 + arg2 + arg3
NameError: name 'arg3' is not defined

对于我的应用程序来说,能够用这种方式定义函数会更好。还有其他的方法,但随后必须向每个函数的主体添加更多的代码。


这有点老生常谈,但这种装饰的实现似乎可以满足您的需要。必须使arg3变量显示为全局变量,因为它在func()定义生成的字节代码中被引用为一个变量,因此它实际上不被视为函数参数,但在这种情况下似乎无关紧要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from functools import wraps

def addarg(arg):
    def decorate(func):
        @wraps(func)
        def wrapped(*args):
            global_vars, local_vars = {'func': func, 'args': args}, {}
            func.__globals__.update(arg3=arg)
            exec('_result = func(*args)', global_vars, local_vars)
            return local_vars['_result']
        return wrapped
    return decorate

@addarg(3)
def func(arg1, arg2):
    return arg1 + arg2 + arg3

if __name__ =="__main__":
    print(func(1, 2))  # -> 6


你可以那样做

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from functools import wraps

def addarg(arg):
    def decorate(func):
        @wraps(func)
        def wrapped(*args):
            return func(*args, arg)
        return wrapped
    return decorate

@addarg(3)
def func(arg1, arg2, *args):
    return arg1 + arg2 + sum(args)


if __name__ =="__main__":
     print(func(1, 2))

这样,您就不会附加到任何名称上,并且可以根据需要应用任意多的装饰器。所以,如果你想增加更多的装饰,它会起作用。例如

1
2
3
4
5
6
7
8
9
10
@addarg(3)
@addarg(3)
@addarg(3)
def func(arg1, arg2, *args):
    return arg1 + arg2 + sum(args)

if __name__ =="__main__":
     print(func(1, 2))

# Output would be 12(1 + 2 + 3 + 3 + 3)