1. 四种作用域
Python 中有四种作用域,分别是:
- Local(局部作用域)
- Enclosing(外层作用域)
- Global(全局作用域)
- Built-in(内建作用域)
当访问一个变量时,Python 会按以下顺序查找: Local => Enclosing => Global => Built-in

上述图示的详细分析,请参考视频教程。
1.1. 局部作用域(Local)
定义:函数的内部就是局部作用域,局部作用域中的变量,只在该函数内部可见。
特点:
- 每次调用函数都会创建一个新的局部作用域。
- 函数运行结束后,局部作用域会随之销毁。
- 局部作用域优先级最高,即:查找一个变量时,Python 会首先在局部作用域中查找。
举例:
1.2. 外层作用域(Enclosing)
定义:如果函数中又定义了函数,那么外层函数的作用域,就是内层函数的 Enclosing 作用域。
特点:
- 只有当函数“嵌套定义”时才会出现。
- 内层函数可以读取外层函数变量。
- 想修改外层变量必须使用
nonlocal。
举例:
修改外层变量:
1.3. 全局作用域(Global)
定义:
.py文件就是全局作用域,全局作用域中的变量,在当前.py文件的任何位置都可以访问。特点:
- 全局变量只在当前
.py文件中可见。
- 函数内部可以使用
global关键字修改全局变量。
例子:
如果要修改:
1.4. 内建作用域(Built-in)
定义:Python 预先定义好的东西,会放在内建作用域中,所有
.py文件都可以直接使用。特点:
- 所有
.py文件都能直接使用其中的名称。
- 例如:
print、len、range、sum、max等。
- 查找优先级最低,即:查找一个变量时,内建作用域是“最后一道防线”。
例子:
2. 闭包
2.1. 前置知识
前置知识 1: 局部作用域的生命周期。
- 每次调用函数时,都会创建一个新的局部作用域。
- 函数执行完毕后,该作用域就会被销毁,其中的局部变量,也会随之释放。
在上述代码中:每次调用
outer(),num 都会重新生成,不会保存上一次的值。前置知识 2:内层函数访问外层变量。
- 【内层函数】可以访问到【外层函数】作用域中的变量。
- 访问外层变量不用
nonlocal,修改外层变量时要使用nonlocal。
如下代码验证了:理论上
num变量应该在outer函数调用完销毁,但实际并没有。2.2. 什么是闭包
- 闭包 = 内层函数 + 被内层函数引用的外层变量。
- 产生闭包的三个条件如下:
- 必须有函数嵌套
- 内层函数使用了外层函数的变量
- 外层函数返回内层函数
2.3. 闭包的基本形式
在上述代码的基础上,我们在
inner函数中不仅要打印num,还要去修改num(修改时记得加上nonlocal num)。最终发现:num的值居然还可以一直增加,所以截至目前,证明了一件事:本应该随着outer函数调用结束而死去的num,并没有死去,并且inner函数依然可以对num进行读取和修改。所以我们观察如下的代码形式,完全符合上述的闭包产生条件,所以此时就出现了闭包。2.4. 闭包是如何保存外层变量的?
外层变量会被保存到 闭包单元(cell)中,例如下面代码中,那些被
inner函数所使用到的outer函数中的局部变量,会被封存在闭包单元(cell) 中,这些cell组成一个 __closure__ 元组,保存在了inner函数上。思考:内层函数
inner会保存外层函数outer中所有的数据吗? 不会,只会保存inner中所用到的。2.5. 注意点
1. 每次获得一个新闭包,互不影响(闭包之间是互相独立的)。
- 外层变量为可变对象时仍互不影响。
2.6. 闭包的优点
- 可以“记住”状态:不用全局变量,也不用写类,就能在多次调用之间保存数据。
- 可以做“配置过的函数”:先传一部分参数,把环境固定住,得到一个定制版函数。
- 可以实现简单的“数据隐藏”:外层变量对外不可见,只能通过内层函数访问。
- 是装饰器(decorator)等高级用法的基础。
小案例:使用闭包实现对文字的美化效果。
2.7. 闭包的缺点
- 理解成本较高:对初学者不太友好,滥用会让代码难读。
- 如果闭包里引用了很大的对象,又长期不释放,可能会增加内存占用。
- 很多场景下,其实用【类 + 实例属性】会更清晰,闭包不一定是最优解。
例如将上述的文字美化效果,用类实现:
3. 装饰器
📎 参考文章
- 一些引用
- 引用文章
欢迎您在底部评论区留言,一起交流~
Loading...
