(六)闭包保存状态与 nonlocal
1. 返回一个记住外层名称的函数
希望分别建立“30 分钟达标”和“45 分钟达标”两套判断,可以让函数生成函数:
# 每次调用 make_checker,都建立自己的 goal 绑定和内部函数。
def make_checker(goal):
def check(minutes):
return minutes >= goal
return check
daily = make_checker(30)
weekend = make_checker(45)
print(daily(35), weekend(35))输出 True False。make_checker(30) 返回后,daily 指向内部的 check,它仍能读取那次外层调用的 goal。函数连同保留的外层绑定形成闭包。保留的是需要继续访问的绑定,不是把整个外层调用永久挂在调用栈上。
2. 用 nonlocal 更新外层函数的绑定
# add 的 total 来自外层 make_counter;每个计数器保留自己的状态。
def make_counter():
total = 0
def add(minutes):
nonlocal total
total += minutes
return total
return add
first = make_counter()
second = make_counter()
print(first(20), first(35), second(10))输出 20 55 10。nonlocal total 表示这次赋值更新最近一层已经存在的外层函数绑定。它与 global 指向的位置不同:global 选模块层,nonlocal 选外层函数层。
图中第一份状态从 0 → 20 → 55,第二份从 0 → 10。如果删除 nonlocal,total += minutes 会被当作先读取、再重新绑定局部 total,而这个局部值还没有建立。
3. 循环中的闭包与延迟读取
# 在外层函数里循环定义,让三个 lambda 捕获同一个 goal 绑定。
def make_checks():
checks = []
for goal in [30, 45, 60]:
checks.append(lambda minutes: minutes >= goal)
return checks
checks = make_checks()
print([check(40) for check in checks])
# 默认值在每次执行 lambda 定义时选定,分别保留当时的整数对象。
def make_fixed_checks():
checks = []
for goal in [30, 45, 60]:
checks.append(lambda minutes, target=goal: minutes >= target)
return checks
fixed = make_fixed_checks()
print([check(40) for check in fixed])第一行结果是 [False, False, False];循环结束时,外层 goal 已经是 60,三个函数后来都读取它。第二行是 [True, False, False];三个默认值分别是 30、45、60。
这里的默认值是整数,保留后不会被原地修改。若保存的是列表,仍然会共享那个列表对象。另一种写法是在循环里调用前面的 make_checker(goal),让每次外层调用产生独立绑定。嵌套作用域与名称