一、闭包
1. 作用域
学习闭包前先要回顾一下作用域的知识:作用域一般分为全局作用域和局部作用域,对应使用的就是全局变量和局部变量。
值得注意的是在局部作用域中可以访问全局变量,而全局作用域中是不能访问局部变量的。
原因:Python 的底层存在一个内存回收空间,用于加快计算机的运行;变量同样需要占用内存,当局部作用域所代表的函数在运行过后,其内部的变量和程序会被 Python 当作已经使用过的内存进行回收。
既然是范围方面的程序就会有解决的方法,当程序中需要在全局作用域实现对局部变量的访问时,就要用到闭包了。
2. 闭包的含义
**闭包:**在函数嵌套的前提下,内部的函数使用了外部函数的变量,外部函数又返回了内部函数,这时这个内部函数就是闭包。
3. 闭包的构成
如何区分一个程序是否存在闭包,闭包一般有三步构成的条件:
- 存在函数的嵌套
- 有内部函数的对于外部函数的引用
- 有外部函数对内部函数进行返回(即 return)
这里需要注意的是,闭包引用了外部函数的变量,所以外部函数的变量在运行完外部函数之后其内存并没有得到释放,会消耗内存。
经典闭包的构成例子:
- 存在嵌套函数:outer 是外部函数,inner 是内部函数
- 内部函数 inner 引用了外部函数 outer 中的自由变量 num
- 外部函数 outer 返回了内部函数 inner 的引用
- 当 outer() 执行完毕后,返回的 inner 函数依然可以访问并保留对变量 num 的绑定,形成闭包
4. 闭包实现对外部变量的修改
这里要引用一个新的关键词 nonlocal;它是用于声明函数内部修改函数外部的变量,这个变量不是全局变量。
它和 global 关键字不同就在于 global 在函数内部声明变量,但这个变量是全局变量。二者在实际的使用是相似的,只是使用的场景和作用不同。
闭包实现修改的案例:
res 作为外部函数的局部变量,num 作为内部函数的变量;res+=num 就能直接修改 res 变量,再打印 res 引用外部函数变量,outer() 再返回内部函数 inner 形成闭包,最后全局使用外部函数,可以看到这时的 f(1) 和 f(2) 中 res 发生了变化,而且是递进改变的。
二、装饰器
1. 什么是装饰器
装饰器:在不改变现有函数代码和函数调用的前提下,实现给函数增加额外的功能。
装饰器本质上也是一个闭包函数。
2. 装饰器的定义
register 就是一个装饰器函数,接收函数 fn 为参数;@register 语法就是将装饰器应用于 login 函数中。
调用 login 时其实是调用了装饰器 register 中的 inner 函数,所以先打印开始注册,然后打印登录成功。
3. 装饰器的简单使用
这个装饰器功能用于可以计算下面 demo() 函数运行完的执行时间。
4. 装饰器的形式
带参数的装饰器
关于不定长参数,在之前的 Python 学习中已经了解了,这里不多阐述。
带返回的装饰器
通用装饰器
一般装饰器形式以这个为准。
可以看到装饰器的形式一般都大差不差,按照闭包的形式遵循一定的规则,然后调用函数即可。
5. 装饰器的扩展
使用装饰器传递参数
基本语法:
代码:
效果如下:
类装饰器
基本语法:
代码:
效果如下:
其实就是在装饰器的基础上用到了类的方法。

