VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • Python和Lua的默认作用域以及闭包

默认作用域

前段时间学了下Lua,发现Lua的默认作用域和Python是相反的。Lua定义变量时默认变量的作用域是全局(global,这样说不是很准确,Lua在执行x = 1这样的语句时会从当前环境开始一层层往上查找x,只有在找不到x的情况下才定义全局变量)的,而Python定义变量时默认变量的作用域是局部(local)的(当前块)。另外,Lua可以再定义变量时在变量前加上local关键字来定义局部变量,而Python没有类似的关键字,Python的变量只能定义在当前块中。

我们知道,全局变量是不好的,而局部变量是好的,写程序应该尽量使用局部变量。所以一开始时我觉得Python的这种约定比较好,它的优点就是可以少打些字。写Lua程序时不断在心底默念“勿忘local,勿忘local”,然而还是有时会出现几个漏网之鱼并引发了一些神奇的bug。

闭包

第一次意识到Python默认作用域的问题是在使用闭包时碰到的。关于闭包,Lua教程上有一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function new_counter()
  local n= 0
  local function counter()
    n= n+ 1
    return n
  end
  return counter
end
  
c1= new_counter()
c2= new_counter()
print(c1()) -- 打印1
print(c2()) -- 打印1
print(c1()) -- 打印2
print(c2()) -- 打印2

闭包的本质可以参考SICP第三章的环境模型。在这里可以简单的想象为函数counter有一个私有成员n。

现在问题来了:我想用Python实现同样功能的闭包?

首先直接从Lua代码依葫芦画瓢改写成Python代码:

1
2
3
4
5
6
def new_counter():
  n= 0
  def counter():
    n= n+ 1
    return n
  return counter

然后傻眼:这个程序不能运行,第4行访问了未赋值的变量n。出错的原因并非是Python不支持闭包,而是Python的赋值操作访问不了上一层的变量n(实际上,Python认为这是定义局部变量,而非赋值。在Python中定义局部变量与赋值操作在语法上是冲突的,Python干脆只支持可重定义的定义语句)。由于Python默认作用域是局部的,所以当程序运行到n = n + 1时,Python认为这是一个变量定义操作,于是创建了一个(未初始化的)局部变量n——并且顺利地覆盖了new_counter这一层的n——然后试图把n + 1赋值给n,但是n未初始化,n + 1没法计算,所以程序报错。

 

可以用个小技巧来实现闭包赋值的功能:

1
2
3
4
5
6
def new_counter():
  n= [0]
  def counter():
    n[0]= n[0]+ 1
    return n[0]
  return counter

相关教程