什么是闭包:

    在一个函数内部又定义了一个函数,内层函数能够读写外层函数中的变量,外层函数把这个内层函数作为值返回回来。这个内层函数就叫做闭包(Closure)。闭包本质上就是一个函数。

    简单例子:

    1. def outer():
    2. a = 1
    3. def inner():
    4. print(f'外层函数中的变量a的值为:{a}')
    5. return inner

    我们用上面这段代码来解释一下闭包的定义:
    一个函数(outer)内部又定义了一个函数(inner),内层函数inner能够读写外层函数outer中的变量a.外层函数outer把内层函数inner作为值返回。
    闭包(closure) - 图1
    可以看到,直接运行outer()以后,返回的是一个函数对象,我们需要再次运行这个函数对象,才能运行最里面的函数的代码。

    由于 Python 有作用域的规定,所以在闭包里面是默认只能读取,但不能修改外层函数的变量。我们来测试一下:
    闭包(closure) - 图2
    当你在闭包里面只有读,没有写的时候,闭包可以正确读取外层的变量值。但是当你尝试给外层变量赋值的时候,如果你在赋值语句上方尝试读取这个变量,就会报错。就像是没有定义变量一样

    并且,即使在赋值语句上方没有读取变量值的操作,你的赋值语句也不能修改外层函数的变量。在闭包中是另外创建一个同名的变量而已,对它的修改不能影响外层变量。

    为了在闭包中修改外层变量,我们需要使用一个关键词:nonlocal,它可以获取上一层的作用域。
    闭包(closure) - 图3
    基于这个特性,我们用闭包来实现一个计算斐波那契数列的程序:

    def calc_fib():
        a = 0
        b = 1
        def calc():
            nonlocal a, b
            a, b = b, a + b
            return a
        return calc
    

    你肯定很奇怪,怎么没有指定返回斐波那契数列第几项啊?

    实际上,你自己想要几项就有几项。我们在外面写个 for 循环,依次获得1-40项:
    闭包(closure) - 图4
    大家注意,每一次我获取值的时候,都是直接运行fib(),不需要传入具体的值。也就是说,这个函数fib它自己知道自己当前运行到第几个值了。

    这种计算斐波拉契数列的方式,速度也非常快,我们来看看计算1-40项,一共需要多长时间:
    闭包(closure) - 图5
    总共需要的时间是秒,也就是0.000077秒。使用原始递归算法计算第40项,需要36秒。即使使用 C 语言加速,也需要5秒钟。