同为脚本语言,Python 和 JavaScript 在变量作用域方面有一些相似的地方。
不像 PHP,函数内部的变量和外部变量基本是隔绝的,也就是说,函数如果需要处理外部的数据,通常需要通过参数把数据传递进来。(这里暂时不讨论 global 关键字。)
而 Python 和 JavaScript 不一样,如果在函数内部访问一个没有在当前作用域声明的变量,它会继续向外查找,直到找到对应的值或者最终找不到。
那么这样来看,Python 的闭包应该也比较简单了。
像 JavaScript 一样,我们编写类似的代码:
def func1():
a = 1
def func2():
a = a + 1
return a
return func2
re = func1()
print re()
print re()
但是,实际运行之后,结果并没有出现我们预期的 2 和 3,反而出现了这样的错误:
UnboundLocalError: local variable 'a' referenced before assignment
为什么会出现这样的问题?
我们先看看 JavaScript 是如何实现这个闭包的。
function func1(){
var a = 1;
function func2(){
a = a + 1;
return a;
}
return func2;
}
re = func1();
console.log(re());
console.log(re());上面这段代码运行之后,结果如我们预期,会输出:
2
3
这里需要特别注意一下 func2() 中的:
a=a+1;
如果我在前面加上一个 var:
var a=a+1;
那么这段程序的运行结果会变成两个:
NaN
当时在 Firefox 的开发者平台上找到了关于 var 的说明:
Declares a variable, optionally initializing it to a value.
以及:
The scope of a variable declared with var is the enclosing function or, for variables declared outside a function, the global scope.
简单来说,var 声明的变量作用域是当前函数。
所以,在上面的例子中,如果使用:
var a=a+1;
这个 a 就会成为 func2() 中的局部变量,而不会继续使用 func1() 中的 a。
这也是为什么最终会得到 NaN。
回到 Python 的闭包
再回到前面的 Python 例子。
错误信息:
UnboundLocalError: local variable 'a' referenced before assignment
实际上已经告诉了我们问题所在。
Python 会把赋值语句中的变量当作当前函数的局部变量。
也就是说:
a=a+1
这里的 a 出现在赋值语句左边,因此 Python 会把它当成 func2() 的局部变量。
这样,在计算右边的:
func2() 自己的 a 又还没有得到赋值,所以就出现了:local variable 'a' referenced before assignment
这个问题怎么解决呢?
如果是在 Python 3.0 以上,可以在a=a+1之前使用 nonlocala
指定这里的 a 不使用当前函数的局部变量,而是使用外层作用域中的变量。
而在 Python 3.0 以下的版本中,并不支持 nonlocal。
这时候,可以换一种方式,比如把 a 放到一个列表中:
def func1():
a = [1]
def func2():
a[0] = a[0] + 1
return a[0]
return func2
re = func1()
print re()
print re()
这样运行之后,就可以得到:
这里虽然修改的是列表中的元素:
但是没有直接对变量 a 进行重新赋值,因此不会触发前面所说的局部变量问题。
Python和JavaScript闭包的区别
从上面的两个例子可以看到,Python 和 JavaScript 都可以实现闭包,而且变量作用域也有一些相似的地方。
但是,在变量声明和作用域处理上,两者又存在明显的区别。
JavaScript 中,使用 var 会在当前函数中声明变量。
而 Python 中,只要在函数内部对变量进行赋值,就会影响 Python 对这个变量作用域的判断。
所以,通过这两个闭包的例子,也可以进一步理解 Python 和 JavaScript 中变量声明以及变量作用域的相似之处和不同之处。
评论0
欢迎分享你的看法,也欢迎补充不同的实践经验。