简谈 Python3 中的闭包
闭包是指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量
。
闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。
当一个内嵌函数引用其外部作用域的变量,我们就会得到一个闭包. 总结一下,创建一个闭包必须满足以下几点
:
必须有一个内嵌函数
内嵌函数必须引用外部函数中的变量
外部函数的返回值必须是内嵌函数
闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时虽然定义作用域不可用了,但仍能使用那些绑定。
闭包的概念难以掌握,下面通过示例进行理解。
假设我们要实现一个计算移动平均
功能的代码,如何实现呢?
初学者可能会用类
来实现,如示例1:
Average的实例是可调用对象:
下面使用函数式
实现,如示例2:
调用make_averager时,返回一个averager函数对象。每次调用averager时,该对象会把参数添加到series中,然后计算当前平均值,如下所示:
以上两个示例有共通之处:调用Averager()或make_averager()得到一个可调用对象avg,该对象会更新历史值,然后计算当前均值。示例1中,avg是Averager的实例;实例2中是内部函数averager。不管怎样,我们都只需要调用avg(n),把n放入系列值series中,然后重新计算均值。
Averager()类的实例avg在哪里存储历史值很明显,但是第二个示例中的avg函数在哪里寻找series呢
?
注意,series是make_averager函数的局部变量,因为那个函数的定义体中初始化了series = []
。可是,调用avg(10)时,make_averager函数已经返回了,而他的本地作用域也一去不复返了。
在averager函数中,series是自由变量
,指未在本地作用域中绑定的变量,图形化展示如下:
averager的闭包延伸到那个函数的作用域之外,包含对自由变量series的绑定
我们可以审查返回的averager对象,发现Python在__code__
属性(表示编译后的函数定义体)中保存局部变量和自由变量的名称,如下所示
series绑定在返回的avg函数的__closure__
属性中。avg.__closure__
中各个元素对应于avg.__code__.co_freevars
中的一个名称。这些元素是cell对象,有个cell_content
属性,保存着真正的值。这些属性的值如示例所示:
综上,闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时虽然定义作用域不可用了,但仍能使用那些绑定
。
稍微有点编程经验的人会看到,我们实现的计算移动平均值得方法实际上有很大的改进空间,改进代码如下:
补坑推荐阅读:
版权声明: 本文为 InfoQ 作者【王坤祥】的原创文章。
原文链接:【http://xie.infoq.cn/article/a2e8fd379d02c982461243742】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论