Python闭包与nonlocal
《Fluent Python》里说,简单来说,闭包就是一个包含非全局变量且该变量不是在它自己函数体里声明的函数。它不关心函数是否匿名,它关心的是可以访问不在自己函数体里定义的非全局变量。
考虑一个avg平均值函数,如下
1 | >>> avg(10) |
一个使用类的方法如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Averager():
"""
>>> avg = Averager()
>>> avg(10)
10.0
>>> avg(11)
10.5
>>> avg(12)
11.0
"""
def __init__(self):
self.series = []
def __call__(self, new_value):
self.series.append(new_value)
total = sum(self.series)
return total/len(self.series)
一个函数的实现方法如下
1 | def make_averager(): |
这里需要注意的是series相对于averager是一个自由变量,也就是这个变量不属于局部变量。
1 | >>> avg.__code__.co_varnames ('new_value', 'total') |
而每一个avg.__code__.co_freevars
里的变量的值都保存在avg.__closure__
中,其值在avg.__closure__[0].cell_contents
1 | >>> avg.__code__.co_freevars |
所以,闭包就是一个可以从自由变量里获取变量的函数。
下面看看为什么会引入nonlocal这个关键字,以及它的作用。
如果我们想办法消除series变量,可以编写如下函数。
1 | def make_averager(): |
但函数执行时会报错
1 | >>> avg = make_averager() |
这是因为变量作用域的原因。count += 1
相当于count = count + 1
, 此时count会被当做局部变量,而不是引用外面一层的count, 而averager函数里并没有声明count变量,于是报错。可以参看Python之dis模块
在Python3里添加了nonlocal来解决这个问题。
1 | def make_averager(): |