我创建了一个简单的装饰器,它接收了一个参数(使用函数而不是类),当发生了一些事情时:添加一行代码会破坏前一行的执行。
以下是代码:
def my_decorator(sublabel):
def duration_wrapper(f):
print sublabel
# Uncommenting this code will break the previous line - why?
# if sublabel is None:
# sublabel = f.func_name
def wrapped_function(*args, **kwargs):
return f(*args, **kwargs)
return wrapped_function
return duration_wrapper
@my_decorator('me')
def myf(): pass
myf()
取消注释这些代码行会导致此异常:
Traceback (most recent call last):
File "test.py", line 16, in <module>
@my_decorator('me')
File "test.py", line 4, in duration_wrapper
print sublabel
UnboundLocalError: local variable 'sublabel' referenced before assignment
任何人都可以解释为什么取消注释这两行代码会破坏它吗?
答案 0 :(得分:7)
装饰器是闭包,来自封闭范围引用的封闭范围的所有标签必须在封闭范围内保持静态。如果您希望封闭范围中的变量可以从封闭范围中变化,则需要将其包装在可变对象中,例如:
def my_decorator(sublabel):
sublabel = [sublabel]
def duration_wrapper(f):
print sublabel[0]
if sublabel[0] is None:
sublabel[0] = f.func_name
def wrapped_function(*args, **kwargs):
return f(*args, **kwargs)
return wrapped_function
return duration_wrapper
正如@Bakuriu在评论中指出的那样,Python 3引入了nonlocal
来消除这种限制,你也可以使sublabel
成为一个全局来解决这个问题,但是全局变量通常是一个坏主意
答案 1 :(得分:2)
您的问题可以简化为这个简单的例子
sublevel = 1
def func():
print sublevel
sublevel = 2
func()
问题是当您在内部范围内分配变量时,您正在屏蔽外部范围(全局级别或父级功能级别范围),阅读文档以查看python scope rules的工作方式,需要注意的重要事项是{{1 }}
因此,在我的示例中,您需要在调用print之前说scopes are determined statically
,您需要使用global sublevel
关键字(python 3 +)