python 装饰器中的变量范围
Scope of variables in python decorator
我在 Python 3 装饰器中遇到了一个非常奇怪的问题。
如果我这样做:
def rounds(nr_of_rounds):
def wrapper(func):
@wraps(func)
def inner(*args, **kwargs):
return nr_of_rounds
return inner
return wrapper
它工作得很好。但是,如果我这样做:
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
我得到:
while nr_of_rounds > 0:
UnboundLocalError: local variable 'nr_of_rounds' referenced before assignment
换句话说,如果我在return中使用它,我可以在内部函数中使用nr_of_rounds
,但我不能用它做任何其他事情。这是为什么?
由于 nr_of_rounds
由 闭包 获取,您可以将其视为 "read-only" 变量。如果你想写入它(例如递减它),你需要明确地告诉 python——在这种情况下,python3.x nonlocal
关键字会起作用。
作为一个简短的解释,Cpython 在遇到函数定义时所做的是查看代码并决定所有变量是 local 还是 非本地。局部变量(默认情况下)是出现在赋值语句、循环变量和输入参数左侧的任何内容。每个其他名称都是非本地名称。这允许一些巧妙的优化1。要像使用本地变量一样使用非本地变量,您需要通过 global
或 nonlocal
语句明确地告诉 python。当 python 遇到它认为 应该 是本地但实际上不是的东西时,你会得到一个 UnboundLocalError
.
1Cpython 字节码生成器将本地名称转换为数组中的索引,以便本地名称查找(LOAD_FAST 字节码指令)与索引数组加上正常的字节码开销一样快。
目前还没有办法对封闭函数作用域中的变量做同样的事情,但是 Python 3 引入了一个新的关键字,"nonlocal" 它将以类似于全局的方式工作,但是对于嵌套函数作用域。
所以在你的情况下只需使用:
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
欲了解更多信息 Short Description of the Scoping Rules?
我在 Python 3 装饰器中遇到了一个非常奇怪的问题。
如果我这样做:
def rounds(nr_of_rounds):
def wrapper(func):
@wraps(func)
def inner(*args, **kwargs):
return nr_of_rounds
return inner
return wrapper
它工作得很好。但是,如果我这样做:
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
我得到:
while nr_of_rounds > 0:
UnboundLocalError: local variable 'nr_of_rounds' referenced before assignment
换句话说,如果我在return中使用它,我可以在内部函数中使用nr_of_rounds
,但我不能用它做任何其他事情。这是为什么?
由于 nr_of_rounds
由 闭包 获取,您可以将其视为 "read-only" 变量。如果你想写入它(例如递减它),你需要明确地告诉 python——在这种情况下,python3.x nonlocal
关键字会起作用。
作为一个简短的解释,Cpython 在遇到函数定义时所做的是查看代码并决定所有变量是 local 还是 非本地。局部变量(默认情况下)是出现在赋值语句、循环变量和输入参数左侧的任何内容。每个其他名称都是非本地名称。这允许一些巧妙的优化1。要像使用本地变量一样使用非本地变量,您需要通过 global
或 nonlocal
语句明确地告诉 python。当 python 遇到它认为 应该 是本地但实际上不是的东西时,你会得到一个 UnboundLocalError
.
1Cpython 字节码生成器将本地名称转换为数组中的索引,以便本地名称查找(LOAD_FAST 字节码指令)与索引数组加上正常的字节码开销一样快。
目前还没有办法对封闭函数作用域中的变量做同样的事情,但是 Python 3 引入了一个新的关键字,"nonlocal" 它将以类似于全局的方式工作,但是对于嵌套函数作用域。
所以在你的情况下只需使用:
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
欲了解更多信息 Short Description of the Scoping Rules?