什么是闭包
其实,闭包指延伸了作用域的函数,其中包含函数定义体中引用、但是
不在定义体中定义的非全局变量。函数是不是匿名的没有关系,关键是
它能访问定义体之外定义的非全局变量。
示例 7-8 average_oo.py:计算移动平均值的类
class Averager(): def __init__(self): self.series = [] def __call__(self, new_value): self.series.append(new_value) total = sum(self.series) return total/len(self.series)
示例 7-9 是函数式实现,使用高阶函数 make_averager。
def make_averager(): series = [] def averager(new_value): series.append(new_value) total = sum(series) return total/len(series) return averager
注意,这两个示例有共通之处:调用 Averager() 或
make_averager() 得到一个可调用对象 avg,它会更新历史值,然后
计算当前均值。在示例 7-8 中,avg 是 Averager 的实例;在示例 7-9
中是内部函数 averager。不管怎样,我们都只需调用 avg(n),把 n
放入系列值中,然后重新计算均值。
Averager 类的实例 avg 在哪里存储历史值很明显:self.series 实例
属性。但是第二个示例中的 avg 函数在哪里寻找 series 呢?
注意,series 是 make_averager 函数的局部变量,因为那个函数的定
义体中初始化了 series:series = []。可是,调用 avg(10)
时,make_averager 函数已经返回了,而它的本地作用域也一去不复
返了。
在 averager 函数中,series 是自由变量(free variable)。这是一个
技术术语,指未在本地作用域中绑定的变量,参见图 7-1。
图 7-1:averager 的闭包延伸到那个函数的作用域之外,包含自由
变量 series 的绑定
审查返回的 averager 对象,我们发现 Python 在 __code__ 属性(表示
编译后的函数定义体)中保存局部变量和自由变量的名称,如示例 7-11所示。
综上,闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,
这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定。
注意,只有嵌套在其他函数中的函数才可能需要处理不在全局作用域中
的外部变量。