要理解“闭包函数和嵌套函数的区别在于 __closure__
属性”,首先需要明确 嵌套函数 和 闭包函数 的基础关系——闭包函数是“满足特定条件的嵌套函数”,而 __closure__
属性正是闭包函数区别于普通嵌套函数的“标志性特征”,它记录了闭包对外部变量的“捕获痕迹”。下面分步骤拆解这句话的含义:
第一步:先理清两个基础概念
在讲 __closure__
之前,必须先明确“嵌套函数”和“闭包函数”的关系,避免混淆:
1. 嵌套函数(Nested Function)
指 在一个函数内部定义的函数,它是“函数嵌套”这一语法结构的统称,不强调是否引用外部变量。
例如,下面的 inner_func
就是一个嵌套函数,但它没有引用外部函数的变量,因此不是闭包:
def outer_func():# 内部定义的嵌套函数def inner_func():print("我是嵌套函数,没引用外部变量")return inner_func # 返回嵌套函数# 调用外部函数,得到嵌套函数
func = outer_func()
func() # 输出:我是嵌套函数,没引用外部变量
2. 闭包函数(Closure Function)
是 满足两个条件的嵌套函数:
- 条件1:嵌套在另一个函数(外部函数)内部;
- 条件2:引用了外部函数的局部变量(即“捕获”了外部变量),且外部函数执行结束后,这些变量仍能被内层函数访问。
例如,下面的 inner_func
引用了外部函数的 x
,因此是闭包函数:
def outer_func(x):# 嵌套函数,引用了外部变量 xdef inner_func(y):return x + y # 引用外部函数的局部变量 xreturn inner_func # 返回嵌套函数(此时它是闭包)# 外部函数执行结束,返回闭包函数
add5 = outer_func(5)
# 调用闭包时,仍能访问外部函数的 x=5
print(add5(3)) # 输出 8(5+3)
第二步:__closure__
属性——闭包的“身份标识”
普通嵌套函数(未引用外部变量)和闭包函数(引用了外部变量)的核心区别,体现在 __closure__
属性上:
- 普通嵌套函数:没有
__closure__
属性(或__closure__
为None
),因为它没有引用外部变量,无需“保留”外部变量; - 闭包函数:有
__closure__
属性,其值是一个 元组,元组中的每一项都是一个cell
(“单元格”)对象——每个cell
对应闭包引用的一个外部变量。
示例:对比普通嵌套函数和闭包的 __closure__
# 1. 普通嵌套函数(无外部变量引用)
def outer1():def inner1():print("普通嵌套函数")print("inner1 的 __closure__:", inner1.__closure__) # 查看 __closure__return inner1# 2. 闭包函数(引用外部变量)
def outer2(x):def inner2(y):return x + y # 引用外部变量 xprint("inner2 的 __closure__:", inner2.__closure__) # 查看 __closure__return inner2# 测试普通嵌套函数
inner1 = outer1()
# 输出:inner1 的 __closure__: None(无外部变量引用)# 测试闭包函数
inner2 = outer2(5)
# 输出:inner2 的 __closure__: (<cell at 0x0000023...: int object at 0x...>,)
# 元组中有1个 cell 对象,对应引用的外部变量 x
第三步:cell_contents
——读取闭包捕获的外部变量
__closure__
元组中的每个 cell
对象,都有一个 cell_contents
属性,用于 获取该 cell 对应的外部变量的值。
这解释了“闭包能在外部函数结束后仍访问外部变量”的原理:闭包通过 __closure__
中的 cell
对象,“保留”了对外部变量的引用,而 cell_contents
就是访问这些变量的“入口”。
示例:用 cell_contents
查看闭包捕获的变量
def outer(x, y):# 闭包引用了外部变量 x 和 ydef inner(z):return x + y + zreturn inner# 外部函数执行,传入 x=2, y=3,返回闭包
closure = outer(2, 3)# 1. 查看 __closure__:元组有2个 cell(对应 x 和 y)
print("closure.__closure__:", closure.__closure__)
# 输出:(<cell at 0x0000023...: int object at 0x...>, <cell at 0x0000023...: int object at 0x...>)# 2. 用 cell_contents 读取每个外部变量的值
print("第一个 cell 的值(x):", closure.__closure__[0].cell_contents) # 输出 2
print("第二个 cell 的值(y):", closure.__closure__[1].cell_contents) # 输出 3# 3. 调用闭包,验证变量确实被保留
print(closure(4)) # 输出 9(2+3+4)
第四步:总结核心区别
通过 __closure__
属性,我们能清晰区分“普通嵌套函数”和“闭包函数”:
特征 | 普通嵌套函数(未引用外部变量) | 闭包函数(引用外部变量) |
---|---|---|
__closure__ 属性 |
None (无此属性或值为 None) |
非 None 的元组,存储 cell 对象 |
cell 对象与 cell_contents |
无 | 元组中每个 cell 对应一个捕获的外部变量,cell_contents 可读取变量值 |
外部变量的生命周期 | 外部函数执行结束后,变量被销毁 | 外部函数结束后,变量通过 cell 被保留,闭包可继续访问 |
简单来说:
__closure__
是闭包的“身份证”——普通嵌套函数没有这张“身份证”(__closure__=None
),而闭包函数通过这张“身份证”(__closure__
元组)记录了它“带走”的外部变量,cell_contents
则是查看这些“带走的变量”的窗口。这也是闭包能突破“函数执行完局部变量销毁”常规的关键技术细节。