概念上的东西这里就不详细解释了,为什么需要闭包(Closures)呢?个人理解就是某些情况下你需要在外部访问函数内部的变量时,闭包就该大显身手了。闭包的一种典型表现就是函数内部定义了新的函数。这里举几个例子以记录使用闭包时的几个注意点,例子来源于这里,大神用javascript写的示例,这里改成python的,也顺便说说遇到的坑。

例子1:闭包中局部变量是引用而非拷贝

1
2
3
4
5
6
7
8
9
def say667():
'''闭包中局部变量是引用而非拷贝'''
num = 666
def saynum():
print "in saynum:", num
num += 1
return saynum
temp = say667()
temp()

输出结果是667,有C、C++基础的应该不难理解这个吧?

例子2:多个函数绑定同一个闭包,因为他们定义在同一个函数内。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def setupSomeGlobals():
'''多个函数绑定同一个闭包,因为他们定义在同一个函数内。'''
num = [666]
def get_num():
print "num:", num
def add_num():
num[0] += 1
def set_num(x):
num[0] = x
global getnum
getnum = get_num
global addnum
addnum = add_num
global setnum
setnum = set_num
setupSomeGlobals()
getnum()
addnum()
getnum()
setnum(10)
getnum()

注意这个例子中使用py2有坑,见这里。如果你定义一个比如整型这种不可变类型,则在py2中报错“UnboundLocalError: local variable ‘num’ referenced before assignment”。解决办法就是定义列表、字典甚至类,在py3中使用nonlocal关键字。

例子3:当在一个循环中赋值函数时,这些函数将绑定同样的闭包

1
2
3
4
5
6
7
8
9
10
11
12
def buildlist(mylist):
'''当在一个循环中赋值函数时,这些函数将绑定同样的闭包,可以理解为上一个例子的变体'''
result = []
for one in mylist:
item = "item:%s" % one
result.append(lambda :(item,one))
return result
def testlist():
fnlist = buildlist([1,2,3])
for i in fnlist:
print i()
testlist()

这个例子中python和javascript的返回结果不同,因为python的for循环结束时就是值就是最后一个,而不会再+1。

例子4:外部函数所有局部变量都在闭包内,即使这个变量声明在内部函数定义之后。

1
2
3
4
5
6
7
8
def sayAlice():
'''外部函数所有局部变量都在闭包内,即使这个变量声明在内部函数定义之后。'''
def say():
print "hello,",name
name = "Alice"
return say
helloAlice = sayAlice()
helloAlice()

这个我以前还真没注意到……

例子5:每次函数调用的时候创建一个新的闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def newClosure(someNum,someRef):
'''每次函数调用的时候创建一个新的闭包'''
num = [someNum] #注意这里,理由同示例2
arr = [1,2,3]
ref = someRef
def fun(x):
num[0] += x
arr.append(x)
print "num:",num,"arr:",arr,"ref:",ref
return fun
closure1=newClosure(40,{'someVar':'closure 1'});
closure2=newClosure(1000,{'someVar':'closure 2'});
closure1(5)
closure2(-10)

至于应用,我第一个想到的就是python中各种装饰器了。