@spiritnotes
2016-02-18T06:58:19.000000Z
字数 1249
阅读 1510
Python
闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。
下面就是一个闭包的例子,可以看到inc3这个函数中引用到了外层传人的变量n,而这个n就是定义中的自由变量,在这里为3,而我们如果再定义一个inc2=inc(2),则在inc2中其自由变量为2。
def inc(n):
def inner_func(x):
return x+n
return inner_func
inc3 = inc(3)
assert inc3(2) == 5
我们可以通过函数的 __closure__ 来查看其绑定的自由变量。
>>> inc3.__closure__
(<cell at 0x0000000002E8F468: int object at 0x000000005FE3EFB0>,)
在Python中所有变量均是引用,而引用再绑定到实际的对象上,那么定义闭包时绑定的是自由变量,还是其指向的对象呢?我们设计如下代码在定义闭包函数后再更改自由变量,让其指向不同的对象,其执行结果显示为后面的对象,因此说明定义函数其绑定的是变量而非对象。
def out(n):
def inner():
print(n)
n = []
return inner
out(1)()
其实这正是Python中迟绑定的机制,即闭包中变量的值只有在内部函数被调用时才会进行查询。
某些情况下,我们想更改外部的自由变量,在Python2中是无法做到的,而在Python3中引入了nonlocal关键字,可以更改自由变量。
def out(n):
def inner():
nonlocal n
n += 1
print(n)
return inner
x = out(1)
x(), x()
# 结果
# 2
# 3
lambda也是同样的闭包原理,因此在使用lambda时也要注意闭包函数执行时才会查询实际值,否则可能出现意料外的情况。
def get_map_funs(n):
return [lambda x:x**i for i in range(n)]
print([j(2) for j in get_map_funs(6)])
# 结果 [32, 32, 32, 32, 32, 32]
该问题有两种修改方式:
def get_map_funs(n):
return (lambda x:x**i for i in range(n))
print([j(2) for j in get_map_funs(6)])
#结果 [1, 2, 4, 8, 16, 32]
def get_map_funs(n):
return [lambda x,i=i:x**i for i in range(n)]
print([j(2) for j in get_map_funs(6)])
#结果 [1, 2, 4, 8, 16, 32]