了解闭包之前,有必要先弄清楚 python 的函数作用域
变量作用域
- 局部:local
- 全局:global
- 非全局:nonlocal
Example-1
无声明的情况下,赋值即私有,若外部有相同变量名则将其遮挡
1 | a = 1 |
1
2
3
2
1
Example-2
1 | a = 1 |
1
1
1
1
1
Example-3
内层函数可以通过声明的方式直接修改外部变量
1 | a = 1 |
1
2
3
2
2
Example-4
位于最内层的函数,通过 global 声明,会越过中间层,直接修改全局变量
1 | a = 1 |
1
2
3
2
3
Example-5
global 声明其实是一种绑定关系,意思是告诉解释器,不用创建新变量了,我用的是外面那个
1 | a = 1 |
1
2
3
3
3
Example-6
位于最内层的函数,如果仅想要修改中间层变量,而不是全局变量,可使用 nonlocal 关键字
1 | a = 1 |
1
2
3
3
1
Example-7
nonlocal 只能绑定在中间层定义的变量,如果中间层变量被声明为全局变量,则会报错
1 | a = 1 |
File "<ipython-input-8-b4968ab8f1e1>", line 10
nonlocal a
^
SyntaxError: no binding for nonlocal 'a' found
闭包
定义: 延伸了作用域的函数(能访问定义体之外定义的非全局变量), 闭包是一种函数,它会保留定义函数时存在的外层非全局变量的绑定。
avg
函数如下所示:
1 | >>> avg(10) |
用类实现
1 | class Averager(): |
10.0
10.5
11.0
用闭包实现
1 | def make_averager(): |
10.0
10.5
11.0
优化闭包
1 | def make_averager(): |
10.0
10.5
11.0
闭包有什么用呢?
- 共享变量的时候避免使用了不安全的全局变量
- 允许将函数与某些数据关联起来,类似于简化版面向对象编程
- 相同代码每次生成的闭包,其延伸的作用域都彼此独立(计数器,注册表)
- 函数的一部分行为在编写时无法预知,需要动态实现,同时又想保持接口一致性
- 较低的内存开销:类的生命周期远大于闭包
- 实现装饰器