递归闭包中的作用域错误

时间:2010-03-25 15:07:44

标签: python recursion scope closures

为什么这样做:

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

3 个答案:

答案 0 :(得分:14)

该错误似乎不能很好地描述根本问题。迈克解释了这些消息,但这并没有解释根本原因。

实际问题是在python中你不能分配给已关闭的变量。所以在function2中'a'是只读的。当你分配给它时,你创建一个新的变量,正如迈克指出的那样,你在写之前就读过了。

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

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

所以al是不可变的,但它的内容不是,你可以在不创建新变量的情况下更改它们。

答案 1 :(得分:5)

应该注意,这是Python中的语法故障。 Python本身(在字节码级别)可以很好地分配给这些变量;在2.x中根本没有语法表示你想要这样做。它假定如果您在嵌套级别中分配变量,则表示它是本地的变量。

这是一个巨大的缺点;能够分配到闭包是至关重要的。我和charlieb的hack多次合作过。

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/

答案 2 :(得分:3)

在非工作代码段中,当您说“a”时,您可以指定a -= 1。因此,在function2内,a是该范围的本地,而不是封闭范围。 Python的闭包是词法 - 它不会在a的框架中动态查找function2,如果尚未分配,则在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

(当然还是{{1}} ;-))