递归闭包中的作用域错误

9 投票
3 回答
2784 浏览
提问于 2025-04-15 20:51

为什么这个可以运行:

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 个回答

5

需要注意的是,这在Python中是一个语法问题。Python本身(在字节码层面)可以正常给这些变量赋值;只是2.x版本没有语法来表示你想这么做。它假设如果你在一个嵌套的层级中给变量赋值,你的意思是这个变量是局部的。

这真是个大缺陷;能够给闭包(即函数内部的函数)赋值是非常重要的。我已经用charlieb的技巧多次解决了这个问题。

Python 3通过一个名字很奇怪的“nonlocal”关键字修复了这个问题:

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/

14

这个错误信息看起来并没有清楚地说明问题的根本原因。Mike 解释了这些信息,但这并没有解释真正的问题所在。

实际的问题是,在 Python 中,你不能对被封闭的变量进行赋值。所以在 function2 中,'a' 是只读的。当你试图给它赋值时,其实是创建了一个新的变量,正如 Mike 所指出的,你是在写之前先读取了它。

如果你想从内部作用域给外部变量赋值,你需要用一些小技巧,像这样:

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

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

撰写回答