python decorator中的变量范围

时间:2020-12-30 23:36:03

I'm having a very weird problem in a Python 3 decorator.

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

If I do this:

如果我这样做:

def rounds(nr_of_rounds):
    def wrapper(func):
        @wraps(func)
        def inner(*args, **kwargs):
            return nr_of_rounds
        return inner
    return wrapper

it works just fine. However, if I do this:

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

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

I get:

我明白了:

while nr_of_rounds > 0:
UnboundLocalError: local variable 'nr_of_rounds' referenced before assignment

In other words, I can use nr_of_roundsin the inner function if I use it in a return, but I can't do anything else with it. Why is that?

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

2 个解决方案

#1


15  

Since nr_of_rounds is picked up by the closure, you can think of it as a "read-only" variable. If you want to write to it (e.g. to decrement it), you need to tell python explicitly -- In this case, the python3.x nonlocal keyword would work.

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

As a brief explanation, what Cpython does when it encounters a function definition is it looks at the code and decides if all the variables are local or non-local. Local variables (by default) are anything that appear on the left-hand side of an assignment statement, loop variables and the input arguments. Every other name is non-local. This allows some neat optimizations1. To use a non-local variable the same way you would a local, you need to tell python explicitly either via a global or nonlocal statement. When python encounters something that it thinks should be a local, but really isn't, you get an UnboundLocalError.

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

1The Cpython bytecode generator turns the local names into indices in an array so that local name lookup (the LOAD_FAST bytecode instruction) is as fast as indexing an array plus the normal bytecode overhead.

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

#2


0  

Currently there is no way to do the same for variables in enclosing function scopes, but Python 3 introduces a new keyword, "nonlocal" which will act in a similar way to global, but for nested function scopes.
so in your case just use like:
def inner(*args, **kwargs): nonlocal nr_of_rounds lst = [] while nr_of_rounds > 0: lst.append(func(*args, **kwargs)) nr_of_rounds -= 1 return max(lst) return inner
For more info Short Description of the Scoping Rules?

目前没有办法对包含函数作用域的变量做同样的事情,但是Python 3引入了一个新的关键字“nonlocal”,它将以类似于global的方式运行,但是对于嵌套的函数作用域。所以在你的情况下只需使用:def inner(* args,** kwargs):nonlocal nr_of_rounds lst = []而nr_of_rounds> 0:lst.append(func(* args,** kwargs))nr_of_rounds - = 1 return max (lst)return inner更多信息范围规则的简短描述?

#1


15  

Since nr_of_rounds is picked up by the closure, you can think of it as a "read-only" variable. If you want to write to it (e.g. to decrement it), you need to tell python explicitly -- In this case, the python3.x nonlocal keyword would work.

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

As a brief explanation, what Cpython does when it encounters a function definition is it looks at the code and decides if all the variables are local or non-local. Local variables (by default) are anything that appear on the left-hand side of an assignment statement, loop variables and the input arguments. Every other name is non-local. This allows some neat optimizations1. To use a non-local variable the same way you would a local, you need to tell python explicitly either via a global or nonlocal statement. When python encounters something that it thinks should be a local, but really isn't, you get an UnboundLocalError.

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

1The Cpython bytecode generator turns the local names into indices in an array so that local name lookup (the LOAD_FAST bytecode instruction) is as fast as indexing an array plus the normal bytecode overhead.

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

#2


0  

Currently there is no way to do the same for variables in enclosing function scopes, but Python 3 introduces a new keyword, "nonlocal" which will act in a similar way to global, but for nested function scopes.
so in your case just use like:
def inner(*args, **kwargs): nonlocal nr_of_rounds lst = [] while nr_of_rounds > 0: lst.append(func(*args, **kwargs)) nr_of_rounds -= 1 return max(lst) return inner
For more info Short Description of the Scoping Rules?

目前没有办法对包含函数作用域的变量做同样的事情,但是Python 3引入了一个新的关键字“nonlocal”,它将以类似于global的方式运行,但是对于嵌套的函数作用域。所以在你的情况下只需使用:def inner(* args,** kwargs):nonlocal nr_of_rounds lst = []而nr_of_rounds> 0:lst.append(func(* args,** kwargs))nr_of_rounds - = 1 return max (lst)return inner更多信息范围规则的简短描述?