【问题标题】:Using a decorator to bring variables into decorated function namespace使用装饰器将变量带入装饰函数命名空间
【发布时间】:2017-01-28 17:35:48
【问题描述】:

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

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

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

我已经尝试过,建议 here 能够将参数传递给装饰器:

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

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

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

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

【问题讨论】:

  • 为什么你想这样做?您要解决的实际问题是什么?对我来说,这似乎是一个无法阅读代码的秘诀。
  • 密切相关:Getting the block of commands that are to be executed in the with statement;那里的解决方案也适用于装饰器,因为 arg3 只会作为全局变量被发现。
  • 我试图让用户将常微分方程系统定义为一系列函数,以类似于现有软件的方式进行集成。
  • 装饰器会引入一些切片对象或常量用于 numpy 数组,这些数组指定特定元素或维度来子集数组,而不是在有意义的情况下对切片或元素进行硬编码。但你是对的,可能有更好的方法来做到这一点。

标签: python function namespaces python-decorators


【解决方案1】:

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

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

【讨论】:

  • 这对我有用!使用exec 确实看起来很老套,但问题也有点老套。将等待看看是否有任何其他答案出现。
  • 还有更多hacky方法可以做到这一点,比如修改修饰函数的字节码。在answer 中有一个关于将self 参数自动添加到类的方法中的问题(尽管不一定使用装饰器)。这种方法的一个优点是结果会运行得更快,因为它是如此低级。
【解决方案2】:

你可以这样做

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

这样您就不会附加任何名称,并且可以应用任意数量的装饰器。因此,如果您想添加更多装饰器,它会起作用。例如

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

【讨论】:

    猜你喜欢
    • 2014-12-13
    • 1970-01-01
    • 1970-01-01
    • 2014-07-21
    • 1970-01-01
    • 2020-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多