闭包
在 Python 中,函数也是一个对象。因此,我们在定义函数时,可以再嵌套定义一个函数,并将该嵌套函数返回,比如:
from math import pow
def make_pow(n):
def inner_func(x): # 嵌套定义了 inner_func
return pow(x, n) # 注意这里引用了外部函数的 n
return inner_func # 返回 inner_func上面的代码中,函数 make_pow 里面又定义了一个内部函数 inner_func,然后将该函数返回。因此,我们可以使用 make_pow 来生成另一个函数:
>>> pow2 = make_pow(2) # pow2 是一个函数,参数 2 是一个自由变量
>>> pow2
<function inner_func at 0x10271faa0>
>>> pow2(6)
36.0我们还注意到,内部函数 inner_func 引用了外部函数 make_pow 的自由变量 n,这也就意味着,当函数 make_pow 的生命周期结束之后,n 这个变量依然会保存在 inner_func 中,它被 inner_func 所引用。
>>> del make_pow # 删除 make_pow
>>> pow3 = make_pow(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'make_pow' is not defined
>>> pow2(9) # pow2 仍可正常调用,自由变量 2 仍保存在 pow2 中
81.0像上面这种情况,一个函数返回了一个内部函数,该内部函数引用了外部函数的相关参数和变量,我们把该返回的内部函数称为闭包(Closure)。
在上面的例子中,inner_func 就是一个闭包,它引用了自由变量 n。
闭包的作用
闭包的最大特点就是引用了自由变量,即使生成闭包的环境已经释放,闭包仍然存在。
闭包在运行时可以有多个实例,即使传入的参数相同。
利用闭包,我们还可以模拟类的实例。
这里构造一个类,用于求一个点到另一个点的距离:
用闭包来实现:
可以看到,结果是一样的,但使用闭包实现比使用类更加简洁。
常见误区
闭包的概念很简单,但实现起来却容易出现一些误区,比如下面的例子:
在该例子中,我们在每次 for 循环中创建了一个函数,并将它存到 funcs 中。现在,调用上面的函数,你可能认为返回结果是 1, 2, 3,事实上却不是:
为什么呢?原因在于上面的函数 f 引用了变量 i,但函数 f 并非立刻执行,当 for 循环结束时,此时变量 i 的值是3,funcs 里面的函数引用的变量都是 3,最终结果也就全为 3。
因此,我们应尽量避免在闭包中引用循环变量,或者后续会发生变化的变量。
那上面这种情况应该怎么解决呢?我们可以再创建一个函数,并将循环变量的值传给该函数,如下:
小结
闭包是携带自由变量的函数,即使创建闭包的外部函数的生命周期结束了,闭包所引用的自由变量仍会存在。
闭包在运行可以有多个实例。
尽量不要在闭包中引用循环变量,或者后续会发生变化的变量。
参考资料
Last updated
Was this helpful?