在python中,我可以写:
def func():
x = 1
print x
x+=1
def _func():
print x
return _func
test = func()
test()
当我运行它时,输出是:
1
2
由于_func可以访问func中定义的“x”变量。右...
但如果我这样做:
def func():
x = 1
print x
def _func():
x+=1
print x
return _func
test = func()
test()
然后我收到一条错误消息: UnboundLocalError:分配前引用的局部变量'x'
在这种情况下,_func似乎无法“看到”“x”变量
问题是:为什么在第一个例子中打印x“看到”“x”变量,而数学运算符x + = 1会抛出异常?
我不明白为什么......
答案 0 :(得分:8)
检查此答案:https://stackoverflow.com/a/293097/1741450
可以访问除本地函数变量之外的作用域中的变量,但如果没有进一步的语法,则无法将其重新转换为新参数。相反,赋值将创建一个新的局部变量,而不是影响父范围中的变量。例如:
答案 1 :(得分:3)
考虑到Python是一种“解释”语言,人们自然会假设如果一个变量已经在外部作用域中定义,那么同一个变量仍然可以在内部作用域中访问。事实上,在你的第一个例子中,内部_func
只打印x
它就可以了。
但是,关于Python的不明显之处在于,这并不是确定变量范围的确切方式。 Python在编译时分析哪些变量应该被视为范围的“本地”,这取决于它们是否被分配到该范围内。在这种情况下,赋值包括扩充赋值运算符。因此,当Python将第二个示例中的内部_func
编译为字节码时,它会看到x += 1
并确定x
必须是_func
的局部变量。除了当然,因为它的第一个赋值是一个增强赋值,所以本地没有x
变量来增加,你得到UnboundLocalError
。
另一种看待这种情况的方法可能是写出:
def _func():
print x
x = 2
此处再次因为_func
包含行x = 2
,它会将x
视为该函数范围内的局部变量而不视为{{ 1}}在外部函数中定义。因此,x
也应该产生print x
。
您可以使用dis
模块更详细地检查这一点,以显示为该函数生成的字节码:
UnboundLocalError
>>> dis.dis(_func)
2 0 LOAD_FAST 0 (x)
3 PRINT_ITEM
4 PRINT_NEWLINE
3 5 LOAD_CONST 1 (2)
8 STORE_FAST 0 (x)
11 LOAD_CONST 0 (None)
14 RETURN_VALUE
操作码用于“快速”查找局部变量,绕过较慢,更通用的名称查找。它使用一个指针数组,其中每个局部变量(在当前堆栈帧中)与该数组中的索引相关联,而不是通过字典查找等。在上面的示例中,LOAD_FAST
操作码的唯一参数是LOAD_FAST
- 在这种情况下是第一个(也是唯一的)本地。
您可以检查函数本身(特别是其底层代码对象),该代码中使用了一个局部变量,并且与之关联的变量名称为0
:
'x'
这就是>>> _func.__code__.co_nlocals
1
>>> _func.__code__.co_varnames
('x',)
能够报告dis.dis
的方式。同样适用于0 LOAD_FAST 0 (x)
操作码。
为了理解Python中的变量范围,这些都不是必须知道的,但是知道幕后发生了什么可能会有所帮助。
正如其他一些答案中已经提到的,Python 3引入了STORE_FAST
关键字,该关键字根据对本地范围内该变量的赋值,阻止了名称与局部变量的编译时绑定。
答案 2 :(得分:0)
您无法重新绑定在Python 2中关闭的变量(您可以在Python 3中使用nonlocal
)。如果我必须在Python 2中执行您正在执行的操作,那么我将执行以下解决方法:
class Pointer(object):
def __init__(self, val):
self.val = val
def func():
px = Pointer(1)
print px.val
px.val += 1
def _func():
print px.val
return _func
test = func()
test()
基本上我将需要变换的任何值放到一个对象上 - 有时我使用一个包含一个元素的列表 - 然后重新编写代码,以便所有发生的是方法调用。以上实际上相当于:
class Pointer(object):
def __init__(self, val):
self.val = val
def func():
px = Pointer(1)
print px.val
px.__setattr__('val', px.val + 1)
def _func():
print px.val
return _func
test = func()
test()