python变量作用域的小问题

最近在写python,做一个邮箱相关的工作,在变量作用域上栽了跟头。
错误代码如下:

def foo1():
    a = 1
    def foo2():
        a += 1
    foo2()
    print a

此时执行foo1会报错:UnboundLocalError: local variable ‘a’ referenced before assignment
当时我在想难道闭包函数作用域这么有限?在下面我又作了测试:

def foo1():
    a = 1
    def foo2():
        print a
        #a += 1
    foo2()
    print a

结果是输出两个1,这很奇怪,foo2其实是可以访问a的。
参考了一篇文章写在世界末日的Python的小问题
python的变量访问依然采用的是像C那种里面覆盖外面的机制,它分local,enclosing,global,builtin四个域,分别是局部变量,闭包变量,全局变量和内建变量。
在foo2里面print a,就会去找a是啥,先在local里面找,木有然后就到enclosing里面找,找到a=1,所以就输出1
现在在foo2执行a+=1,问题就来了,这其实是一个赋值表达式,python新搞一个变量其实是不需要声明的,没声明的话必然默认就是local了,那么就对local的a执行a+1,这就囧了,因为local的a在引用之前(即a=a+1的第二个a)并没被赋值过。[local variable ‘a’ referenced before assignment]
总结一下就是能看到值是啥,但是不能改。
python 2.x中有一些办法:(听说python 3.x有nonlocal关键字了…)
1. 别名:

def foo1():
    a = 1
    def foo2():
        b = a
        b += 1
    foo2()
    print a

2. 容器:这里没有出现问题是因为a是一个引用,a的第一个变量是存在的,可以引用,引用完再赋值显然没有[local variable xxx referenced before assignment]的问题…(好像是这么个意思)

def foo1():
    a = [1,]
    def foo2():
        a[0] += 1 #a[0] = a[0] + 1
    foo2()
    print a[0]

最后我自己就用列表那种办法了…
其实说了这么多我还是理解不能…

——

我想过,比如我在里面引用a的时候,它会去找a的,找不到local a就找到enclosing的 a了,最后赋值a+1给a,形成的结果按道理可以是a(local) = a(enclosing) + 1,这不就符合逻辑了。
之前写nodejs的时候看过javascript变量作用域的原理,有这么个想法。解释器相关,解释器先作的词法分析语法分析啥的,然后在某一部确定好了作用域,之后才执行的,在前面这几步的分析上是搞不出a这种一会闭包一会局部的奇怪分析的。理解不了可以看这段我用来验证的代码:

def foo1():
    a = 1
    def foo2():
        print a
        a += 1
    foo2()
    print a

如果足够聪明的话,我知道a可以看到但不可修改,而python又是逐行解释执行的,那么在foo2里面会先print a,之后在a+=1抛出异常。(如果理解不了这个那么只能说明你是一个优秀的C程序员,此地不宜久留,出门左拐不送=。=)
事实不是这样,会直接抛出异常(汗…),但是这和C完全两码事。
原因在于解释器前几步的分析中已经在表中确定了a就是局部变量了(通过a+=1的赋值操作),之后print a的时候它会直接查个表,开始输出a,这个工作其实相当与print也是引用了a,然后才输出的,所以还是那个[UnboundLocalError: local variable ‘a’ referenced before assignment]的错误。异常在print a这行就提前抛出了,而不是a+=1。

然后上面这一段其实也是我自己yy的,我没仔细研究过python这方面,只是透过现象猜本质,如果有误求指出…

—–

今天我洗了被套…阳台晒被子景象甚为壮观…太阳很给力!

Tags :

2 thoughts on “python变量作用域的小问题”

    1. Guido本人貌似不太推崇函数式编程,他认为这个是unpythonic,这些都造成了代码可读性的降低。闭包在实现装饰器、新式类等先进特性时很有作用。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

Click the right image To submit your comment: