Python的闭包与nonlocal

标签:Python

熟悉JavaScript的程序员经常会在内部函数中访问外部函数的私有变量,方法非常简单,只要不用var重新声明该变量,那么JavaScript解释器就会在外层名字空间里寻找对应的变量。

可在Python中却不是这样,这让我非常头疼。简单来说,在Python 2.x中,闭包只能读外部函数的变量,而不能改写它。

举例来说,这样是合法的:
def a():
  x = 0
  def b():
    print locals()
    y = x + 1
    print locals()
    print x, y
  return b

a()()
结果:
{'x': 0}
{'y': 1, 'x': 0}
0 1
而这样是非法的:
def a():
  x = 0
  def b():
    print locals()
    y = x + 1
    print locals()
    x = 1
    print x, y
  return b

a()()
结果:
{}
Traceback (most recent call last):
  File "C:\Documents and Settings\Administrator\Desktop\oo.py", line 11, in <mod
ule>
    a()()
  File "C:\Documents and Settings\Administrator\Desktop\oo.py", line 5, in b
    y = x + 1
UnboundLocalError: local variable 'x' referenced before assignment
可以看到,print locals()和y = x + 1这2步并没有对x赋值,但x就已经找不到了。
同时也可看出,Python代码在解释执行时,并非像BASIC一样一句一句解释,而会将一整个代码块预先做些处理。

要在Python 2.x中解决这个问题,目前只能使用全局变量:
x = 0

def a():
  global x
  x += 1
  def b():
    global x
    x += 1
    print x
  return b

a()()
结果:
2
但全局变量在任何语言中都不被提倡,因为它很难控制,这也是我之前所说的头疼的原因。此外,在函数内访问全局变量需要用global声明,而且连读取都要,这比JavaScript麻烦多了。

为了解决这个问题,Python 3.x引入了nonlocal关键字(详见The nonlocal statement)。
只要在闭包内用nonlocal声明变量,就可以让解释器在外层函数中查找变量名了,例如:
def a():
  x = 0
  def b():
    nonlocal x
    x += 1
    print x
  return b

a()()
结果:
1
虽然声明方式和JavaScript正好相反,但总算是解决了使用全局变量的问题。只可惜Python 2.x中应该是看不到了…

顺便八卦一下,使用nonlocal作为关键字的原因是为了避免与已有的源代码冲突,以下就是Python SVN在2006年11月5日的扫描结果:
nonlocal    0
use         2
using       3
reuse       4
free        8
outer     147

4条评论 你不来一发么↓ 顺序排列 倒序排列

    向下滚动可载入更多评论,或者点这里禁止自动加载

    想说点什么呢?