摄影:产品经理产品经理的厨艺越来越好了
一个函数内部又定义一个函数,内层函数能够读写外层函数中的变量,外层函数把这个内层函数作为值返回出来。这个内层函数就叫做闭包(Closure)。
闭包本质上就是一个函数。
我们来看一个简单例子:
def outer(): a = 1 def inner(): print(f'外层函数中的变量 a 的值为:{a}') return inner
我们用上面这段代码来解释一下闭包的定义:
一个函数(outer
)内部又定义一个函数(inner
),内层函数inner
能够读写外层函数outer
中的变量a
。外层函数outer
把内层函数inner
作为值返回。
我们来运行一下这段代码:
可以看到,直接运行outer()
以后,返回的是一个函数对象,我们需要再次运行这个函数对象,才能运行最里面的函数的代码。
由于 Python 有作用域的规定,所以在闭包里面是默认只能读取,但不能修改外层函数的变量。我们来测试一下:
当你在闭包里面只有读,没有写的时候,闭包可以正确读取外层的变量值。但是当你尝试给外层变量赋值的时候,如果你在赋值语句上方尝试读取这个变量,就会报错。就像是没有定义变量一样。
并且,即使在赋值语句上方没有读取变量值的操作,你的赋值语句也不能修改外层函数的变量。在闭包中是另外创建一个同名的变量而已,对它的修改不能影响外层变量。
为了在闭包中修改外层变量,我们需要使用一个关键词:nonlocal
,它可以获取上一层的作用域。
我们来看一下:
基于这个特性,我们用闭包来实现一个计算斐波那契数列的程序:
def calc_fib(): a = 0 b = 1 def calc(): nonlocal a, b a, b = b, a + b return a return calc
你肯定很奇怪,怎么没有指定返回斐波那契数列第几项啊?
实际上,你自己想要几项就有几项。我们在外面写个 for 循环,依次获得1-40项:
大家注意,每一次我获取值的时候,都是直接运行fib()
,不需要传入具体的值。也就是说,这个函数fib
它自己知道自己当前运行到第几个值了。
这种计算斐波拉契数列的方式,速度也非常快,我们来看看计算1-40项,一共需要多长时间:
总共需要的时间是秒,也就是0.000077秒。大家对比我们之前的一篇文章:一日一技:立竿见影地把你的 Python 代码提速7倍我们已经知道,使用原始递归算法计算第40项,需要36秒。即使使用 C 语言加速,也需要5秒钟。
而使用闭包,只需要0.000077秒,速度足足提高了10万倍不止。可以称得上是降维打击了。