__closure__:闭包的“身份证”

要理解“闭包函数和嵌套函数的区别在于 __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):
    # 嵌套函数,引用了外部变量 x
    def inner_func(y):
        return x + y  # 引用外部函数的局部变量 x
    return 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  # 引用外部变量 x
    print("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 和 y
    def inner(z):
        return x + y + z
    return 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 则是查看这些“带走的变量”的窗口。这也是闭包能突破“函数执行完局部变量销毁”常规的关键技术细节。

posted @ 2025-10-11 01:08  wangya216  阅读(19)  评论(0)    收藏  举报