2010-03-25 63 views
8

为什么这项工作:作用域错误

def function1():                            
     a = 10                              
     def function2(): 
      print a 
     function2() 

但这并不:

def function1(): 
    a = 10 
    def function2(): 
     print a 
     a -= 1 
     if a>0: 
      function2() 
    function2() 

我得到这个错误:

UnboundLocalError: local variable 'a' referenced before assignment 

回答

13

似乎误差不很描述根本问题。迈克解释了这些信息,但这并不能解释根本原因。

实际的问题是,在python中,你不能指定关闭的变量。所以在函数2中,'a'是只读的。当你指定它时,你创建了一个新的变量,正如Mike指出的那样,在你写之前你已经读过了。

如果你想从内部范围分配给到外变量,你必须欺骗,像这样:

def function1(): 
    al = [10] 
    def function2(): 
     print al[0] 
     al[0] -= 1 
     if al[0]>0: 
      function2() 
    function2() 

所以人是不可改变的,但它的内容不是,你可以改变他们,而无需创建新变量。

+2

实际上,这是在设计这个功能,你不能分配给非本地范围的关键点。 (注意:'al'是* mutable *;这就是为什么这是有效的。) – 2010-03-25 15:36:44

+2

为了清楚起见,我认为重要的是区分变量和al包含的值。它总是回到我的指针,所以让我说这个;您不能将al指向新列表,但可以更改al指向的列表的内容。 al - > [v1,v2,v3] al不能更改,但v1,v2和v3可以更改。 Mike绝对正确,这使得al可变,因为在我们的术语中al *是*列表而不是指向列表的指针。 – charlieb 2010-03-25 16:21:56

+0

+1非常好的答案。 – 2010-03-26 04:39:44

2

在非工作片段,当您说“a -= 1”时,您分配给a。因此,function2a在本地范围内,不在封闭范围内。 Python的闭包是词汇 - 它不会动态地在function2的框架中查找a,并且它没有被分配,请在function1的框架中查找它。

请注意,这不取决于递归性或使用闭包。考虑这个功能

def foo(): 
    print a 
    a = 4 

的调用它可以把你的UnboundLocalError过的例子。 (如果没有a = 4,它将使用全球a或者如果没有,则提出NameError。)因为a可能在该范围内分配,所以它是本地的。


如果我设计这个功能,我可能会使用的方法更像

def function1(): 
    a = 10 
    def function2(a=a): 
     print a 
     a -= 1 
     if a > 0: 
      function2(a) 
    function2() 

(或课程for a in xrange(10, -1, -1): print a ;-))的

5

应该指出,这是Python中的语法错误。 Python本身(在字节码级别)可以分配给这些变量就好了;在2.x中根本没有语法来表示你想这样做。它假定如果你在嵌套层次上赋值给变量,你的意思是它是一个局部变量。

这是一个巨大的缺点;能够分配到闭包是最基本的。我已经用charlieb的黑客多次解决了这个问题。

Python 3中修复了这个用很笨拙为“非本地”的文章:

def function1(): 
    a = 10 
    def function2(): 
     nonlocal a 
     print(a) 
     a -= 1 
     if a>0: 
      function2() 
    function2() 
function1() 

这是非常差的,这句法仅在3.x中可用;大多数人被困在2.x中,并且必须继续使用黑客来解决这个问题。这非常需要回溯到2.x.

http://www.python.org/dev/peps/pep-3104/