python decorator中的变量范围

Ben*_*Ben 17 python decorator python-3.x python-decorators

我在Python 3装饰器中遇到了一个非常奇怪的问题.

如果我这样做:

def rounds(nr_of_rounds):
    def wrapper(func):
        @wraps(func)
        def inner(*args, **kwargs):
            return nr_of_rounds
        return inner
    return wrapper
Run Code Online (Sandbox Code Playgroud)

它工作得很好.但是,如果我这样做:

def rounds(nr_of_rounds):
    def wrapper(func):
        @wraps(func)
        def inner(*args, **kwargs):
            lst = []
            while nr_of_rounds > 0:
                lst.append(func(*args, **kwargs))
                nr_of_rounds -= 1
            return max(lst)
        return inner
    return wrapper
Run Code Online (Sandbox Code Playgroud)

我明白了:

while nr_of_rounds > 0:
UnboundLocalError: local variable 'nr_of_rounds' referenced before assignment
Run Code Online (Sandbox Code Playgroud)

换句话说,nr_of_rounds如果我在返回时使用它,我可以在内部函数中使用,但我不能用它做任何其他事情.这是为什么?

mgi*_*son 15

由于闭包nr_of_rounds被拾取,您可以将其视为"只读"变量.如果你想写它(例如减少它),你需要明确告诉python - 在这种情况下,python3.x 关键字将起作用.nonlocal

作为简要说明,Cpython在遇到函数定义时会做什么,它会查看代码并确定所有变量是本地变量还是非本地变量.局部变量(默认情况下)是出现在赋值语句左侧,循环变量和输入参数的任何内容.每个其他名称都是非本地的.这允许一些整洁的优化1.要以与本地变量相同的方式使用非局部变量,您需要通过globalor nonlocal语句显式地告诉python .当python遇到它认为应该是本地的东西,但实际上不是,你会得到一个UnboundLocalError.

1 Cpython字节码生成器将本地名称转换为数组中的索引,以便本地名称查找(LOAD_FAST字节码指令)与索引数组加上正常的字节码开销一样快.

  • 我们可以将`nr_of_rounds`称为自由变量. (2认同)