Python有一个有点让人费解的闭包特性,这个特性的基本特征是在函数中定义嵌套函数,并且外层函数将内层嵌套的函数作为返回值!其实我理解闭包特性实现了好几个特别的功能,给Python函数提供了静态变量,实现了相同函数的不同执行副本,还有python装饰器本质上就是一个闭包。本文记录我对Python闭包特性的理解和应用考虑。
静态变量
Python函数有静态变量吗?没有。要么global,要么就是自己的局部变量,函数退出了这些局部变量就失效。静态变量这个概念来自C语言,用static申明的变量,其地址在程序的data段(永久存在),而且只能够被某个函数访问。在Python中,我们可以用闭包这个特性,实现Python版的静态变量。
def staticVar():
times = 0
def showTimes():
nonlocal times
times += 1
print(times)
return showTimes
f = staticVar()
f()
f()
f()
f()
f()
staticVar函数内定义了一个showTimes函数,这个函数使用nonlocal关键词申明了times不是local的变量,而是staticVar函数内的times。直接运行staticVar函数是没有什么意义的,用f来承接内嵌的showTimes函数,实际上我们要运行的,是内嵌的那个函数,现在名字成了f,然后调用f函数5次,下面是打印出来的效果:
$ python3 cc.py
1
2
3
4
5
times这个变量,就成了f函数(showTimes函数)的静态变量。
执行副本
副本这个词来自网游,用在这里,是我主观臆断的。
很多函数在执行的时候,都会依赖一些变量或标志位,函数根据这些变量或标志位来调整自己的行为,在各种if语句中走不同的分支,输出不同的结果。我们常规的做法,就是给函数不同的入参,或者在函数执行前,先调整全局变量或标志位的值。Python的闭包,给了另外一种选择。
def createRunEnv(location=None):
def run():
print('I am running at',location)
# do something specificly
return run
f_Aisa = createRunEnv('Aisa')
f_Africa = createRunEnv('Africa')
f_Europe = createRunEnv('Europe')
f_Aisa()
f_Africa()
f_Europe()
createRunEnv函数返回的函数,带上了location属性。f_Aisa,f_Africa和f_Europe就是内部函数run的3个不同的副本,调用这3个函数,其内部的location参数是不同的。以上代码执行如下:
$ python3 fb.py
I am running at Aisa
I am running at Africa
I am running at Europe
这种函数副本的好处是,我理解也是封装。将一些参数封装起来,在创建副本的时候确定值,在执行时,只有副本可以访问,以确定自己的运行环境。对同一个函数的不同的执行环境进行打包封装!
以上介绍Python闭包带来的静态变量和函数副本的功能,其实从逻辑上看,没有闭包也可以实现(C语言什么不能实现)。只是有了闭包后,Python代码的书写更加灵活,使用这些特性,代码看起来更加清爽迷人,封装的更加优雅炫酷,当然前提是要能理解这些语法特性。如果传入闭包的参数中有函数,这就是装饰器的本质。
-- EOF --