闭包
2026/7/3大约 5 分钟
1.闭包背景
1.1 为什么需要闭包
在之前学习过程中,调用函数完毕后,函数内的变量就销毁了。
def func():
num = 10
return num
num = func()
print(num + 1) # 11
print(num + 1) # 11
print(num + 1) # 11问题分析:
- 函数执行完毕后,函数栈帧弹出,内部变量被销毁
- 每次调用函数都会重新创建变量,无法保留上次的计算结果
- 如果想实现累加效果(11、12、13...),普通函数无法做到
1.2 闭包的作用
闭包可以延长外部函数局部变量的生命周期,使得外部函数执行完毕后,内部函数依旧可以访问其变量。
说明
当外部函数结束时,正常情况下变量会被销毁;但如果有闭包,内部函数存在的情况下,它依旧可以使用外部函数的变量,相当于变量的生命周期被延长了。
2.闭包入门
2.1 闭包的概念
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且在外部函数中返回了内部函数,这种使用外部函数变量的内部函数称之为闭包。
2.2 闭包的语法格式
def 外部函数名(形参列表):
外部函数的(局部)变量
def 内部函数名(形参列表):
使用外部函数的变量
return 内部函数名2.3 闭包的构成条件
| 条件 | 说明 |
|---|---|
| 有嵌套 | 外部函数嵌套内部函数 |
| 有引用 | 内部函数使用外部函数的变量 |
| 有返回 | 外部函数中,返回内部函数名(对象) |
注意
return 内部函数名 和 return 内部函数名() 是两个概念:
- 前者返回函数对象
- 后者调用函数并返回执行结果
2.4 函数名与函数名()的区别
def get_sum(a, b):
return a + b
# 函数名是对象
print(get_sum) # <function get_sum at 0x...>
# 函数名()是调用函数获取返回值
print(get_sum(10, 20)) # 30
# 函数名可以赋值给变量
my_sum = get_sum
print(my_sum) # <function get_sum at 0x...> 地址相同
print(my_sum(10, 20)) # 30万物皆对象
在 Python 中,单看函数名(不加小括号)也是一个对象,可以赋值给变量。
2.5 闭包入门案例
def fun_outer(num1):
def fun_inner(num2):
sum = num1 + num2
print(sum)
return fun_inner
# 调用外部函数,获取内部函数对象
fun_inner = fun_outer(10)
# 调用内部函数
result = fun_inner(20) # 30调用流程说明:
fun_outer(10)将 10 传给num1,返回fun_inner函数对象fun_inner(20)将 20 传给num2,执行求和操作- 内部函数可以访问外部函数的变量
num1
简化写法:
# 可以直接链式调用
fun_outer(100)(200) # 3003.闭包图解
3.1 内存分析
闭包的内存结构类似于"套房"——外部函数是外层房间,内部函数是里层房间。
┌─────────────────────────────────────┐
│ 方法区 │
│ ┌─────────────────────────────┐ │
│ │ fun_outer (0x01) │ │
│ │ num1 = 10 │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ fun_inner (0x02) │ │ │
│ │ │ num2 │ │ │
│ │ └─────────────────────┘ │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘3.2 调用流程
fn_inner = fun_outer(10) # 步骤1:调用外部函数,返回内部函数对象
fn_inner(1) # 步骤2:调用内部函数,结果为 11
fn_inner(1) # 步骤3:再次调用,结果仍为 11执行过程:
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | fun_outer(10) 进栈 | num1 = 10,返回 fun_inner 对象(0x02) |
| 2 | fun_outer 出栈 | 但 num1 被闭包保留,不会销毁 |
| 3 | fn_inner(1) 进栈 | num2 = 1,计算 num1 + num2 = 11 |
| 4 | fn_inner 出栈 | 打印结果 11 |
| 5 | 再次调用 fn_inner(1) | num1 仍是 10,结果仍是 11 |
注意
闭包中外部函数的变量不会被重新初始化,每次调用内部函数时使用的是同一个变量。
4.nonlocal 关键字
4.1 问题引入
如果想在内部函数中修改外部函数的变量,直接赋值会报错:
def fun_outer():
a = 100
def fun_inner():
a = a + 1 # 报错:UnboundLocalError
print(a)
return fun_inner报错原因:
- 内部函数可以访问外部函数的变量(读取)
- 但不能直接修改外部函数的变量(写入)
- 因为
a = a + 1会被解释为定义一个新的局部变量a
4.2 nonlocal 关键字介绍
nonlocal 是 Python 内置的关键字,可以实现在内部函数中修改外部函数的变量值。
语法:
def fun_outer():
a = 100
def fun_inner():
nonlocal a # 声明使用外部函数的变量 a
a += 1
print(a)
return fun_inner4.3 nonlocal 案例
def fun_outer():
a = 10
def fun_inner():
nonlocal a
a += 1
print(a)
return fun_inner
fun_inner = fun_outer()
fun_inner() # 11
fun_inner() # 12
fun_inner() # 13效果: 每次调用内部函数,变量 a 都会在上次的基础上累加。
4.4 nonlocal 与 global 的区别
| 关键字 | 作用 | 使用场景 |
|---|---|---|
global | 声明使用全局变量 | 在函数内部修改全局变量 |
nonlocal | 声明使用外部函数变量 | 在内部函数中修改外部函数的变量 |
4.5 nonlocal 图解
第1次调用 fun_inner():
┌─────────────────────────────────────┐
│ 外部函数(已出栈,但变量被闭包保留) │
│ a = 10 │
│ │
│ 内部函数进栈: │
│ nonlocal a → 声明使用外部的 a │
│ a += 1 → a = 10 + 1 = 11 │
│ print(a) → 输出 11 │
└─────────────────────────────────────┘
第2次调用 fun_inner():
┌─────────────────────────────────────┐
│ 外部函数变量(已被修改) │
│ a = 11 │
│ │
│ 内部函数进栈: │
│ a += 1 → a = 11 + 1 = 12 │
│ print(a) → 输出 12 │
└─────────────────────────────────────┘5.闭包总结
5.1 闭包的作用
闭包的作用是延长外部函数局部变量的生命周期。
- 外部函数执行完毕后,内部函数依旧可以访问其变量
- 变量的生命周期与内部函数绑定,而非外部函数
5.2 闭包的构成条件
| 条件 | 说明 |
|---|---|
| 有嵌套 | 外部函数嵌套内部函数 |
| 有引用 | 内部函数使用外部函数的变量 |
| 有返回 | 外部函数返回内部函数名 |
5.3 nonlocal 关键字
- 作用:让内部函数可以修改外部函数的变量
- 语法:在内部函数中使用
nonlocal 变量名声明
5.4 注意事项
常见错误
return 内部函数名不要写成return 内部函数名()return语句要与内部函数定义平级,不要写在内部函数内部- 修改外部函数变量时,需要使用
nonlocal关键字声明
