Python3 入门教程
变量作用域与闭包
本教程共 70 篇 · 第 24 篇 · 更新于 2026-07-22 · 约 3 分钟阅读
PythonPython3 入门教程作用域LEGB闭包nonlocal
24. 变量作用域与闭包
本节目标:理解 LEGB 规则,掌握 global 和 nonlocal,学会使用闭包。
LEGB 规则
Python 查找变量时,按这个顺序:
- Local:函数内部
- Enclosing:外层嵌套函数
- Global:模块全局
- Built-in:内置作用域
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # local,L 层找到
inner()
outer()
如果 inner 里没有 x = "local",会去找 outer 里的 x;outer 里也没有,去找 global;再没有,找内置。
global 关键字
想在函数内部修改全局变量,用 global 声明:
count = 0
def increment():
global count
count += 1
increment()
increment()
print(count) # 2
Warning如果不加
global,Python 会把count += 1里的count当作局部变量,但赋值前引用了它,会报UnboundLocalError。
count = 0
def broken():
count += 1 # UnboundLocalError!
nonlocal 关键字
nonlocal 用于修改外层(非全局)函数的变量:
def outer():
x = 10
def inner():
nonlocal x
x = 20
print(f"inner: {x}")
inner()
print(f"outer: {x}")
outer()
# inner: 20
# outer: 20
Note
nonlocal和global的区别:global跳到模块顶层,nonlocal只跳到最近的外层函数。
闭包(Closure)
闭包是指「引用了外部函数变量的内部函数」。即使外部函数已经执行完毕,闭包仍然能访问那些变量。
def make_multiplier(n):
def multiplier(x):
return x * n
return multiplier
times3 = make_multiplier(3)
times5 = make_multiplier(5)
print(times3(10)) # 30
print(times5(10)) # 50
make_multiplier 执行完后,它的局部变量 n 本应该被销毁。但因为返回的 multiplier 还在引用 n,所以 n 被「封闭」在闭包里,继续存活。
Tip闭包的价值在于「数据封装」。不用类,也能实现带状态的功能。
闭包的陷阱:延迟绑定
在循环里创建闭包,容易踩坑:
def make_functions():
funcs = []
for i in range(3):
def func():
return i
funcs.append(func)
return funcs
f1, f2, f3 = make_functions()
print(f1(), f2(), f3()) # 2 2 2,不是 0 1 2
三个闭包都引用了同一个 i,而循环结束时 i 的值是 2。
解决办法:让闭包在定义时就捕获当前值。
def make_functions():
funcs = []
for i in range(3):
def func(x=i): # 默认参数在定义时求值
return x
funcs.append(func)
return funcs
f1, f2, f3 = make_functions()
print(f1(), f2(), f3()) # 0 1 2
或者用 functools.partial:
from functools import partial
def make_functions():
funcs = []
for i in range(3):
funcs.append(partial(lambda x: x, i))
return funcs
什么时候用闭包
闭包适合简单的状态保持场景。如果状态和行为越来越复杂,就该用类了。
# 用闭包实现简单的计数器
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
c = make_counter()
print(c()) # 1
print(c()) # 2
print(c()) # 3
来源:参考了 runoob「Python3 命名空间作用域」、liaoxuefeng「函数式编程」等,改写后所得。