在装饰器包装函数中访问装饰器参数

3 投票
2 回答
1023 浏览
提问于 2025-04-16 11:36

我正在尝试在包装函数中访问我的装饰器参数,但一直没有成功。

我现在有的代码是:

def my_decorator(arg1=False, arg2=None):

    def decorator(method):
        @functools.wraps(method)
        def wrapper(method, *args, **kwargs):
            # do something based on arg1 and arg2
            # accessing one of the two named arguments
            # ends up in a 'referenced before assignment'

            arg1 = arg1 # error
            arg2 = arg2 # error

            newarg1 = arg1 # working
            newarg2 = arg2 # working

            return method(*args, **kwargs)
        return wrapper
    return decorator

然后我会像使用普通装饰器那样使用它。

@my_decorator(arg1=True, arg2='a sting or whatever else')
the_function()

我真的不明白为什么我无法访问装饰器的参数。

2 个回答

0

这要看你怎么使用 arg1arg2。一般来说,闭包(closure)在没有额外操作的情况下是可以正常工作的。不过,如果你在内部函数里重新给它们赋值,Python会认为这是局部变量,这时候你需要告诉它不是。

在Python 3中,你可以用 nonlocal arg1, arg2 来声明。在Python 2中,你需要用点小技巧:把这两个变量放到列表里(在外部函数中用 arg1 = [arg1]),然后在内部函数中用 arg1[0] 来访问。如果你想知道为什么这样做有效,可以去查查关于这个话题的Python问题,或者看看文档(我记得在语言参考的某个地方有提到,我会去找找)。

你的问题是 wrapperself 传给了 method。但是这里没有 self。你需要把它拿出来(即使这样做的话,你也会把装饰器限制在方法上——为什么不让 self 作为 *args 的一部分呢?)。

我不明白你是怎么从“global name self is not defined”这句话中理解出“referenced before assignment”的。

4

你可以使用 arg1arg2,但是不要给它们重新赋值,甚至用“增强型”赋值运算符也不行,因为这样会把它们变成内层函数的局部变量。你收到的错误信息就是在告诉你,你正好做了这个(虽然你没有展示你的代码)。

在 Python 3 中,你可以通过以下方式解决这个问题:

nonlocal arg1, arg2

wrapper() 的某个地方使用。

撰写回答