首页 > 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 |