函数外部声明的变量

我只是试图了解变量作用域是如何工作的,然后遇到了以下情况(所有这些都是从终端运行的) :

x = 1
def inc():
x += 5


inc()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in inc
UnboundLocalError: local variable 'x' referenced before assignment

我在想,也许我的方法中没有 x,所以我尝试了:

def inc():
print(x)


1

这样就行了,现在我知道我可以这么做:

 def inc():
global x
x += 1

这是可行的,但我的问题是,为什么第一个例子失败了?我的意思是,既然 print(x)可以工作,那么 x在函数中是可见的,那么为什么 x += 5会失败呢?

143827 次浏览

当函数为 定义时,决定函数的本地名称:

>>> x = 1
>>> def inc():
...     x += 5
...
>>> inc.__code__.co_varnames
('x',)

在这种情况下,x存在于本地命名空间中。x += 5的执行需要一个 x的预先存在的值(对于整数,它类似于 x = x + 5) ,这在函数调用时失败,因为本地名称是未绑定的——这正是异常 UnboundLocalError被命名为这样的原因。

比较另一个版本,其中 x不是局部变量,因此可以在全局范围内解析它:

>>> def incg():
...    print(x)
...
>>> incg.__code__.co_varnames
()

类似的问题: http://docs.python.org/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value

当 Python 解析函数时,它会注意变量 任务的生成时间。当有一个赋值时,默认情况下它假设该变量是一个局部变量。若要声明赋值引用全局变量,必须使用 global声明。

当您在函数中使用 进入变量时,将使用 LEGB 范围规则查找它的值。


第一个例子

x = 1
def inc():
x += 5
inc()

产生一个 UnboundLocalError,因为 Python 将 inc内部的 x确定为一个局部变量,

而访问 x在您的第二个示例中可以工作

def inc():
print x

因为在这里,根据 LEGB 规则,Python 在本地范围中查找 x,没有找到它,然后在扩展范围中查找它,仍然没有找到它,最后在全局范围中成功地查找它。

与采用“真正的”词法范围的语言不同,Python 选择为变量(无论是 globalnonlocal还是 local)提供特定的“名称空间”。可以说,让开发人员有意识地使用这样的名称空间编写代码更加明确,因此也更容易理解。我认为这种复杂性使得语言更加笨拙,但是我想这完全取决于个人偏好。

下面是一些关于 global的例子:-

>>> global_var = 5
>>> def fn():
...     print(global_var)
...
>>> fn()
5
>>> def fn_2():
...     global_var += 2
...     print(global_var)
...
>>> fn_2()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn_2
UnboundLocalError: local variable 'global_var' referenced before assignment
>>> def fn_3():
...     global global_var
...     global_var += 2
...     print(global_var)
...
>>> fn_3()
7

同样的模式也可以应用于 nonlocal变量,但是这个关键字只能用于后面的 Python 版本。

如果您想知道的话,nonlocal用于变量不是全局的,但不在正在使用的函数定义中的情况。例如,def中的 def,这种情况很常见,部分原因是缺少多语句 lambdas。尽管在早期的 Python 中有一种方法可以绕过这个特性的缺失,但是我依稀记得它涉及到单元素列表的使用..。

注意,写入变量是需要这些关键字的地方。只是读它们并不含糊,因此不需要。除非你有内部 def使用相同的变量名作为外部的,这只是应该避免诚实。