Python: 闭包

1093 查看

闭包的概念

关于什么是闭包,下面是百度百科的解释

闭包是可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)

在Python当中,当函数A返回函数B时, A中的参数和变量都保持在返回函数B中(通过引用计数实现),函数B就是一个闭包,函数A的参数也称为自由变量。

闭包的一些特性

惰性求值

>>> def ret_func(a):
...     def func():
...         return a
...     return func
...
>>> func1 = ret_func(2)
>>>
>>> func1()
2

可以看到,只有在函数调用的时候,才执行函数func1,这就是惰性求值。这个在Django当中的数据查询时的惰性查询就是通过闭包的方式实现的。
每一次调用都返回一个新的函数

>>> def ret_func(a):
...     def func():
...         return a
...     return func
...
>>> func1 = ret_func(2)
>>> func2 = ret_func(2)
>>>
>>> func1 != func2
True

很明显func1 != func2,这也印证每一次调用返回的都是一个新的函数

闭包要注意的问题

不要在闭包当中引用任何会发生改变的变量


在上面的代码当中,函数ret_func_list返回一个list里面保存了3个函数变量,这4个函数都共同的引用了循环变量i, 也就是说它们共享着同一个变量i,i是会改变的,当函数调用时,循环变量i已经是等于2了,因此3个函数返回的都是4。如果,需要在闭包使用循环变量的值的话,把循环变量作为闭包的默认参数或者是通过偏函数来实现。实现的原理也很简单,就是把当把循环变量当参数传入函数时,会申请新的内存。示例代码如下

>>> def ret_func2():
...     arr = []
...     for i in range(4):
...         arr.append(lambda i=i: i*i)
...     return arr
...
>>> results = ret_func2()
>>> func1 = results[0]
>>> func2 = results[1]
>>> func3 = results[2]
>>>
>>> func1()
0
>>> func2()
1
>>> func3()
4