闭包
我们都知道,Python函数是支持嵌套的。如果在一个内部函数中对外部函数作用域(非全局作用域)的变量进行引用,那么内部函数就会被称为闭包。闭包需要满足如下三个条件:
(1)存在于嵌套关系的函数中。
(2)嵌套的内部函数引用了外部函数的变量。
(3)嵌套的外部函数会将内部函数名作为返回值返回。
为了让读者更好地认识闭包,接下来,通过一个案例对闭包进行讲解,具体代码如下:
# 外部函数
def outer(start=0):
count = [start] # 函数内的变量
# 内部函数
def inner():
count[0] += 1 # 引用外部函数的变量
return count[0]
# 返回内部函数的名称
return inner
quote = outer(5)
print(quote())
在以上代码中,定义了一个嵌套函数。其中,outer函数是外部函数,inner是内部函数。
在outer函数中,首先定义了一个表示列表的变量count(该列表只有一个元素),然后定义了一个inner函数,最后将inner函数的名称返回。
在inner函数中,引用了外部函数定义的列表count,并对count的元素进行修改,修改后的列表元素使用return返回。
将调用外部函数outer的结果赋值给变量quote,quote引用的是inner函数占用的内存空间。
当调用了quote函数,实际上就是inner函数。
程序的运行结果如图1所示。
图1 运行结果
下面通过一张图来分析程序执行的过程,如图2所示。
图2 程序执行的过程图
图2描述的具体过程如下:
(1)调用outer函数,并且把5传递给start参数。此时,start的值为5。
(2)由于start引用的值为5,所以count列表中存放的元素为5。
(3)返回内部函数的名称inner。
(4)将调用outer函数返回的inner赋值给quote变量,即quote=inner。此时,quote变量引用的是inner函数占用的内存空间。
(5)调用quote引用的函数,实质上相当于调用inner函数。此时,程序会来到inner函数的定义部分。
(6)让count列表的元素加1,即5+1=6。计算完以后,把得出的结果再次赋值为列表的元素。此时,列表中存放的元素为6。
(7)返回count列表的元素值6。
从变量的生命周期的角度来讲,在outer函数执行结束以后,变量count已经被销毁了。当outer函数执行完后,会再执行内部的inner函数,由于inner函数中使用了count变量,所以程序应该出现运行时错误。然而,程序仍能正常运行,这是为什么呢?究其原因,主要在于函数的闭包会记得外层函数的作用域,在inner函数(闭包)中引用了外部函数的count变量,所以程序是不会释放这个变量的。