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教程上有一段代码:
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代码:
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没法计较,所以措施报错。
可以用个小能力来实现闭包赋值的成果:
def new_counter(): n = [0] def counter(): n[0] = n[0] + 1 return n[0] return counter
这里n[0] = n[0] + 1不会堕落的原因是这里的等号和前面n = n + 1的等号寄义纷歧样。n[0] = n[0] + 1中的等号意思是修改n的某个属性。事实上这个等号最终挪用了list的__setitem__要领。而n = n + 1中的等号意思是在当前情况将n + 1这个值绑定到标记n中(假如当前情况已存在标记n,就包围它)。
别的题外话:打死我不消这种写法,多灾看呐。横竖Python是面向工具语言,要实现个计数器,大不了写个类。
界说与赋值的疏散
先总结一下Python与Lua的默认浸染域的特点:
1、 Lua默认浸染域是全局的,写措施时要紧记local要害字(除非确实要界说全局变量),不小心忘了local也不会提示,就等着纠bug吧。
2、 Python默认浸染域是局部的,固然写措施的思维承担少些,可是丧失了对上层变量赋值的本领(可以改,但会让语言更杂乱)。
看来两种默认浸染域都有问题?小我私家认为,呈现以上问题的原因是:Python和Lua没有实现界说和赋值的疏散。在Python和Lua中,像x = 1这样的语句既可以暗示界说,也可以暗示赋值。其实不可是这两种语言,其实许多高级语言都没有实现界说和赋值的疏散。界说和赋值两者成果上很像,可是它们本质上是有不同的。
下面以x = 1为例表明界说与赋值:
界说的意思是:在当前情况中注册标记x,并初始化为1。假如x已经存在,则报错(不答允重界说)可能包围(答允重界说)。
赋值的意思是:从当前情况开始,一层层往上找直到第一次找到标记x,把它的值修改成1。假如找不到就报错(变量不存在)。
此刻我们稍微修改一下Python来实现界说和赋值的疏散:用“:=”暗示界说,用“=”暗示赋值。然后重写谁人不能运行的new_counter例子(Python中赋值操纵和界说局部变量斗嘴,换句话说,Python其实没有赋值操纵,所以我们只需简朴的把“=”全换成“:=”就行了),看看它错在哪:
def new_counter(): n := 0 def counter(): n := n + 1 return n return counter
这个措施为什么是错的就很明明晰。第4行我们要的是赋值操纵,而非界说操纵。修改成正确的写法:
def new_counter(): n := 0 def counter(): n = n + 1 return n return counter
这样就能正确运行了(前提是有修改版的Python表明器XD)。
#p#分页标题#e#
最后说一些Lua的环境。Lua感受上就把界说和赋值的疏散实现了一半。带有local要害字的等号语句必定是界说了。问题是不带local的等号语句。对付这种语句Lua是这样做的:先试图做赋值,假如赋值失败(变量不存在),就在最外层情况(全局情况)界说变量。也就是说,不带local的等号语句把界说和赋值混在一起了。别的,假如实现了界说和赋值的疏散,就不需要思量默认浸染域的问题了——界说全部是在当前情况下界说,都是界说局部变量。我实在想不出在一个函数体可能什么块中界说全局变量的长处。