问题: 闭包中外部函数的变量,内部函数可以直接修改吗?

👇 点击展开/折叠 面试背诵模板(建议先背这个) 闭包中内层函数不能直接修改外层函数的变量。直接赋值会将其视为内层函数的局部变量,抛出 UnboundLocalError。若需修改,必须用 nonlocal 关键字显式声明该变量来自外层作用域。只读访问(如读取、调用方法)不需要 nonlocal。
---

错误示例 vs 正确示例

1
2
3
4
5
6
7
8
9
10
# ❌ 错误:直接修改外层变量
def outer():
count = 0
def inner():
count += 1 # 此处 count 被视为 inner 的局部变量
return count
return inner

c = outer()
c() # UnboundLocalError: cannot access local variable 'count'
1
2
3
4
5
6
7
8
9
10
11
12
# ✅ 正确:用 nonlocal 声明
def outer():
count = 0
def inner():
nonlocal count # 声明 count 来自外层作用域
count += 1
return count
return inner

c = outer()
print(c()) # 1
print(c()) # 2

修改变量的三种场景对比

操作类型 是否需要 nonlocal 示例
读取外层变量 ❌ 不需要 return x
调用外层变量的方法(不重新赋值) ❌ 不需要 lst.append(1)
重新赋值(=、+=、-= 等) ✅ 必须用 nonlocal count += 1
重新赋值整个变量(如 x = 10) ✅ 必须用 nonlocal nonlocal x; x = 10

为什么需要 nonlocal?

Python 在编译函数体时,如果某个变量在函数内部被赋值(= 左边出现),则默认将其视为局部变量。count += 1 等价于 count = count + 1,对 count 进行了赋值,于是 Python 认为 count 是 inner 的局部变量,但赋值右侧的 count 还未定义,因此报错。nonlocal 告诉 Python:这个变量不在本函数定义,去外层函数中找。

nonlocal vs global 对比

关键字 作用域 查找链 使用场景
nonlocal 最近的外层函数(非全局) 跳过本地 → 逐层向外找,直到全局之前 闭包中修改外层变量
global 全局作用域 跳过本地 → 直接找全局 函数中修改全局变量

常见陷阱:多层嵌套时的 nonlocal

1
2
3
4
5
6
7
8
9
10
11
12
13
def outer():
x = 1
def middle():
x = 2 # middle 的局部变量,遮蔽了 outer 的 x
def inner():
nonlocal x # 这里的 x 指向 middle 的 x(最近的非全局层)
x += 1
return x
return inner
return middle

f = outer()()
print(f()) # 3(middle 中的 x 从 2 变为 3),outer 中的 x 仍为 1