python的嵌套函数中局部作用域问题?

shomy
  • 1.6k

python中赋值操作都会在当前作用域的locals()内部新绑定一个变量。因此如下的代码会报错:

def func1():
    x = 1
    print locals()
    def func2():
        print 'fun2:', locals()
        x += x
        print 'fun2:', locals()

    func2()
    print locals()

if __name__ == '__main__':
    func1()

报错就是

UnboundLocalError: local variable 'x' referenced before ssignment

但是为什么把x改成列表,然后以下面的方式:

def func1():
    x = [1, 2, 3]
    print locals()
    def func2():
        print 'fun2:', locals()
        x[0] += x[0]
        print 'fun2:', locals()

    func2()
    print locals()

if __name__ == '__main__':
    func1()

输出是:

{'x': [1, 2, 3]}
fun2: {'x': [1, 2, 3]}
fun2: {'x': [2, 2, 3]}
{'func3': <function func3 at 0x7f89da00ac08>, 'x': [2, 2, 3], 'func2': <function func2 at 0x7f89da00ab90>}

但是如果把x[0] += x[0]改成x = x[0]又报错了。
求解释啊。。。已经懵了---

回复
阅读 5.4k
2 个回答

func2 在 func1 之内,其作用域的范围等同于闭包(至于什么是闭包,详见我的这篇文章:Python 的闭包和装饰器),因此 x 实际上是在父函数 func1 的作用域内,func2 调用它类似于 func1 调用全局变量,只能读取而不能直接改写 x。但是如果你使用列表对象的 x,则 x[0] 并非 x对象 ,而是 x 的元素,因此可以被改写。
其实这是 python 2 的一个bug,在 python 3 中引入了 nonlocal 语句,其类似于 global 语句,只不过是用来声明某个变量是父函数的那个变量,以便于子函数可以直接改写该变量。
在python 2 中,只有用列表对象了,不过在 python 3 中可以这么写:

def func1():
    x = 1
    print(locals())
    def func2():
        nonlocal x       #加上这一句声明这里的 x 是父函数的 x,之后便可以直接改写 x
        print('fun2:', locals())
        x += x
        print('fun2:', locals())

    func2()
    print(locals())

if __name__ == '__main__':
    func1()

具体详见 这里

已经解决,思路请见第一条回答及其评论。

宣传栏