闭包:捕获外层变量与生命周期
核心概念
闭包(closure)是捕获了外层作用域变量的函数。即使外层函数已经返回,闭包仍然可以访问和修改这些变量。
关键特征:
- 嵌套函数
- 内层函数引用外层函数的变量
- 外层函数返回内层函数
- 被捕获的变量在外层函数返回后仍然存活
第一个闭包
def make_multiplier(n):
def multiplier(x):
return x * n # 引用外层的 n
return multiplier
times3 = make_multiplier(3)
times5 = make_multiplier(5)
print(times3(10)) # 30
print(times5(10)) # 50解释:
make_multiplier(3)返回multiplier函数multiplier函数"记住"了n = 3- 每次调用
times3,都可以访问它创建时的n值 times3和times5捕获的是不同的n
闭包的内部机制
Python 将闭包捕获的变量存储在函数对象的 __closure__ 属性中:
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.__closure__) # (<cell at 0x...: int object at 0x...>,)
print(c.__closure__[0].cell_contents) # 2__closure__ 是一个元组,包含捕获变量的 cell 对象。cell_contents 是实际值。
闭包捕获变量,不是值
def make_functions():
funcs = []
for i in range(3):
funcs.append(lambda: i) # 捕获变量 i,不是值
return funcs
f0, f1, f2 = make_functions()
print(f0()) # 2(不是 0!)
print(f1()) # 2
print(f2()) # 2问题:所有 lambda 捕获的是同一个变量 i,循环结束时 i = 2,所有函数返回 2。
修复方法 1:使用默认参数立即绑定值
def make_functions():
funcs = []
for i in range(3):
funcs.append(lambda x=i: x) # x 在定义时绑定当前 i 的值
return funcs
f0, f1, f2 = make_functions()
print(f0()) # 0
print(f1()) # 1
print(f2()) # 2修复方法 2:使用额外的闭包层
def make_functions():
def make_func(n):
return lambda: n
return [make_func(i) for i in range(3)]
f0, f1, f2 = make_functions()
print(f0()) # 0
print(f1()) # 1
print(f2()) # 2闭包的实际应用
1. 工厂函数
创建配置不同的函数:
def make_power(exponent):
def power(base):
return base ** exponent
return power
square = make_power(2)
cube = make_power(3)
print(square(5)) # 25
print(cube(5)) # 1252. 数据封装(隐藏状态)
def make_account(initial_balance):
balance = initial_balance # 私有变量
def deposit(amount):
nonlocal balance
balance += amount
return balance
def withdraw(amount):
nonlocal balance
if amount > balance:
return "Insufficient funds"
balance -= amount
return balance
def get_balance():
return balance
return {'deposit': deposit, 'withdraw': withdraw, 'balance': get_balance}
account = make_account(100)
print(account['deposit'](50)) # 150
print(account['withdraw'](30)) # 120
print(account['balance']()) # 120外部无法直接访问 balance,只能通过返回的函数接口。
3. 延迟计算
def make_averager():
series = []
def averager(new_value):
series.append(new_value)
return sum(series) / len(series)
return averager
avg = make_averager()
print(avg(10)) # 10.0
print(avg(11)) # 10.5
print(avg(12)) # 11.0series 在多次调用之间保持状态。
4. 回调函数
def make_handler(prefix):
def handler(message):
print(f"{prefix}: {message}")
return handler
error_handler = make_handler("ERROR")
info_handler = make_handler("INFO")
error_handler("File not found") # ERROR: File not found
info_handler("Server started") # INFO: Server started闭包 vs 类
闭包和类可以实现类似功能:
闭包版本:
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter = make_counter()
print(counter()) # 1
print(counter()) # 2类版本:
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
return self.count
counter = Counter()
print(counter.increment()) # 1
print(counter.increment()) # 2何时用闭包:
- 简单的状态封装
- 只有一两个函数
- 函数式编程风格
何时用类:
- 复杂的数据和行为
- 多个方法
- 需要继承或多态
闭包与垃圾回收
闭包捕获的变量只要闭包存在就不会被回收:
def outer():
large_data = [0] * 1000000 # 大对象
def inner():
return large_data[0]
return inner
func = outer() # large_data 不会被回收
print(func()) # 0
del func # 现在 large_data 可以被回收注意:如果不需要捕获大对象,避免在内层函数中引用它。
nonlocal 详解
nonlocal 用于修改闭包捕获的变量:
def make_counter():
count = 0
def increment():
nonlocal count # 声明修改外层 count
count += 1
return count
def reset():
nonlocal count
count = 0
return increment, reset
inc, rst = make_counter()
print(inc()) # 1
print(inc()) # 2
rst()
print(inc()) # 1nonlocal 的规则:
- 只能用于嵌套函数
- 查找最近的外层作用域(不包括全局)
- 如果找不到,报
SyntaxError
nonlocal vs global:
count = 0 # 全局
def outer():
count = 10 # 外层
def use_global():
global count
count += 1
return count
def use_nonlocal():
nonlocal count
count += 1
return count
print(use_global()) # 1(修改全局 count)
print(use_nonlocal()) # 11(修改外层 count)
print(f"Outer count: {count}") # 11
outer()
print(f"Global count: {count}") # 1练习
温度转换器工厂:编写
make_converter(from_unit, to_unit),返回温度转换函数。支持 Celsius 和 Fahrenheit 互转。pythonc_to_f = make_converter('C', 'F') print(c_to_f(0)) # 32.0 print(c_to_f(100)) # 212.0计数器三件套:编写
make_counter(),返回三个函数:increment(),decrement(),value()。pythoninc, dec, val = make_counter() inc() inc() dec() print(val()) # 1修复闭包陷阱:解释并修复以下代码:
pythondef make_multipliers(): return [lambda x: x * i for i in range(5)] funcs = make_multipliers() print([f(2) for f in funcs]) # 期望 [0, 2, 4, 6, 8],实际输出什么?累加器:用闭包实现累加器,支持
add(n)和total()操作:pythonacc = make_accumulator() acc['add'](10) acc['add'](20) acc['add'](30) print(acc['total']()) # 60限流器:编写
make_rate_limiter(max_calls, period),返回装饰器函数,限制函数在period秒内最多调用max_calls次。
完成练习后,打开本章答案页核对。
下一步
理解了闭包,下一节深入参数模型:*args、**kwargs、仅限关键字参数、参数解包的完整规则。
回到第二章概览 · 返回Python 学习路线