当前位置: 首页 > news >正文

__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):# 嵌套函数,引用了外部变量 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 则是查看这些“带走的变量”的窗口。这也是闭包能突破“函数执行完局部变量销毁”常规的关键技术细节。

http://www.hskmm.com/?act=detail&tid=28351

相关文章:

  • Codeforces Round 1057 (Div. 2)
  • “表达式”(Expression)和“语句”(Statement)概念辨析
  • 每日一题 ###121买卖股票的最佳时机
  • 10.10总结
  • LibreChat-图文并茂手把手教你界面配置 | Adorable LibreChat Interface Configuration Guide
  • GAE-广义优势估计算法介绍
  • qemu模拟单片机
  • RAG-检索增强生成
  • “猴子补丁”(monkey patch)跟猴子有关吗?
  • Yapi 使用docker在cenos7上部署教程与基本使用
  • C语言vsC++
  • 20251010 之所思 - 人生如梦
  • 2025.10.10
  • 个人书单-从心流出发,学习积极心理学
  • 等号(=)在C语言和python中有什么区别?
  • AI元人文(十四)之价值共生篇:再论物物交换——作为价值共生基础的元协议
  • 4.布局系统
  • Python clickhouse-driver 类库使用学习总结
  • 虚拟环境QA
  • 计算机系统知识 - 呓语
  • 详解 `a, b = b, a + b`:执行逻辑、常见误区与赋值符号辨析
  • xdown 全能下载
  • 2025.10.10 - 20243867孙堃2405
  • 密码系统设计
  • c#服务安装和卸载等等
  • 进制表示
  • 在AI技术快速实现创意的时代,挖掘用户真实需求成为关键——某知名电池管理工具需求洞察
  • 从梯度提升树到分布式机器学习算法
  • iPhone手机越狱后出现闪退的解决方法
  • 企业推行 OKR 的 10 个关键问题