Python 闭包详解:嵌套函数如何捕获变量
Python 闭包
了解 Python 闭包如何工作、如何定义,以及为何它们对编写简洁高效代码至关重要。
嵌套函数中的非局部变量
探索闭包前,先理解嵌套函数和非局部变量。定义在另一函数内部的函数叫嵌套函数。此类函数可读取其外层作用域的变量。
Python 中这些非局部变量默认只读。要修改它们,必须在嵌套函数中用 nonlocal 显式声明。
下例展示嵌套函数访问非局部变量:
def print_msg(msg):
# 外部函数
def printer():
# 嵌套函数
print(msg)
printer() # 执行
print_msg("Hello")
输出
Hello
嵌套 printer() 函数可访问其外层作用域的 msg 变量。
定义闭包函数
若外部函数返回嵌套函数而非调用它?代码如下:
def print_msg(msg):
def printer():
print(msg)
return printer # 返回嵌套函数
# 存储返回的函数
another = print_msg("Hello")
another()
输出
Hello
注意函数 printer 即使在 print_msg 执行完毕后仍记住 msg 的值。这种现象——函数保留对其定义作用域变量的访问——在 Python 中称为闭包。
进一步说明,删除原函数看返回函数仍工作:
>>> del print_msg
>>> another()
Hello
>>> print_msg("Hello")
Traceback (most recent call last):
... NameError: name 'print_msg' is not defined
返回函数继续工作,因它在闭包中捕获了必要数据。
何时存在闭包?
Python 中满足以下条件存在闭包:
- 嵌套函数定义在另一函数内部。
- 引用其外层函数的变量。
- 被外层函数返回。
何时使用闭包?
闭包提供多种优势:
- 消除全局变量需求,增强封装性。
- 为单方法类提供轻量替代,保持代码简洁。
- 支持装饰器等函数式模式,包装函数需访问原函数参数。
常见示例:创建乘法器函数的工厂。
def make_multiplier_of(n):
def multiplier(x):
return x * n
return multiplier
# 创建特定乘法器
times3 = make_multiplier_of(3)
times5 = make_multiplier_of(5)
print(times3(9)) # 27
print(times5(3)) # 15
print(times5(times3(2))) # 30
输出
27 15 30
Python 装饰器语法高度依赖闭包。编写装饰器时,包装函数捕获被装饰函数,形成闭包。
检查闭包可用 __closure__ 属性。它返回保存闭包值的单元对象元组。例如:
>>> make_multiplier_of.__closure__
>>> times3.__closure__
(<cell at 0x0000000002D155B8: int object at 0x000000001E39B6E0>,)
每个单元的 cell_contents 属性持有闭包捕获的实际值:
>>> times3.__closure__[0].cell_contents
3
>>> times5.__closure__[0].cell_contents
5
理解闭包让你编写更简洁、模块化的 Python 代码,避免不必要的全局变量并拥抱函数式范式。
Python