闭包
概念介绍
- 简单来说就是一个函数定义中引用了函数外定义的变量,并且该函数可以在其定义环境外被执行。这样的一个函数我们称之为闭包。实际上闭包可以看做一种更加广义的函数概念。因为其已经不再是传统意义上定义的函数。
闭包初探
def outer_func():
loc_list = []
def inner_func(name):
loc_list.append(len(loc_list) + 1)
print '%s loc_list = %s' %(name, loc_list)
return inner_func
clo_func_0 = outer_func()
clo_func_0('clo_func_0')
clo_func_0('clo_func_0')
clo_func_0('clo_func_0')
clo_func_1 = outer_func()
clo_func_1('clo_func_1')
clo_func_0('clo_func_0')
clo_func_1('clo_func_1')
- 在python中我们称上面的这个loc_list为闭包函数inner_func的一个自由变量(free variable)。
- 闭包中的引用的自由变量只和具体的闭包有关联,闭包的每个实例引用的自由变量互不干扰。
- 一个闭包实例对其自由变量的修改会被传递到下一次该闭包实例的调用。
闭包陷阱
def my_func(*args):
fs = []
for i in xrange(3):
def func():
return i * i
fs.append(func)
return fs
fs1, fs2, fs3 = my_func()
print fs1()
print fs2()
print fs3()
- 上面这段代码可谓是典型的错误使用闭包的例子。程序的结果并不是我们想象的结果0,1,4。实际结果全部是4。
- 问题的关键就在于在返回闭包列表fs之前for循环的变量的值已经发生改变了,而且这个改变会影响到所有引用它的内部定义的函数。
- 因为在函数my_func返回前其内部定义的函数并不是闭包函数,只是一个内部定义的函数。当然这个内部函数引用的父函数中定义的变量也不是自由变量,而只是当前block中的一个local variable。
正确的写法
def my_func(*args):
fs = []
for i in xrange(3):
def func(_i = i):
return _i * _i
fs.append(func)
return fs
def my_func(*args):
fs = []
for i in xrange(3):
func = lambda _i = i : _i * _i
fs.append(func)
return fs
- 正确的做法便是将父函数的local variable赋值给函数的形参。函数定义时,对形参的不同赋值会保留在当前函数定义中,不会对其他函数有影响。
- 另外注意一点,如果返回的函数中没有引用父函数中定义的local variable,那么返回的函数不是闭包函数。
闭包的应用
def func_dec(func):
def wrapper(*args):
if len(args) == 2:
func(*args)
else:
print 'Error! Arguments = %s'%list(args)
return wrapper
@func_dec
def add_sum(*args):
print sum(args)
# add_sum = func_dec(add_sum)
args = range(1,3)
add_sum(*args)
参考
理解Python闭包概念