__closure__:闭包的“身份证”

news/2025/10/11 1:14:24/文章来源:https://www.cnblogs.com/wangya216/p/19134136

要理解“闭包函数和嵌套函数的区别在于 __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.mzph.cn/news/934500.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Codeforces Round 1057 (Div. 2)

A. Circle of Apple Trees 题意:一个环形数组,每次到一个位置可以选择拿走这个元素或者跳过,然后走到下一个位置。每次拿的数要比之前拿的大,求最多拿多少数。 显然可以从小到大拿,那么答案就是不同数的个数。点击…

“表达式”(Expression)和“语句”(Statement)概念辨析

在编程中,“表达式”(Expression)和“语句”(Statement)是两个基础且容易混淆的概念。它们的核心区别在于是否有返回值以及在代码中的作用,以下从定义、特点、示例三个维度详细说明: 一、定义与核心区别表达式(…

每日一题 ###121买卖股票的最佳时机

仅以此记录所学所想,如有错误,还望指正。 首次尝试 1、我小小的脑子只能想出暴力解法,结果是超时了。 class Solution { public:int maxProfit(vector<int>& prices) {int max=0;for(int i=0;i<prices…

10.10总结

1.将一个数组中的值按逆序重新存放进行了编程联系,生成了对应程序 2.明天学习完善程序细节 3.今天收获满满

LibreChat-图文并茂手把手教你界面配置 | Adorable LibreChat Interface Configuration Guide

@目录🎀 整体介绍 | Overview🍪 配置项详解 | Field Details🎨 开关大全 | All Switches✨ 配置示例 | Cute ExampleConclusion | 结语 🎀 整体介绍 | Overview 今天我们来学习一下,LibreChat的页面基础配置哇…

GAE-广义优势估计算法介绍

一句话总结 GAE 就像「既要稳又要准」的聪明妥协:用多步 TD 误差加权平均,既缓解了 MC 的高方差,又减少了 TD 的单一偏差,通过调节参数(λ)灵活平衡两者的优缺点。MC vs TD 的痛点MC(蒙特卡洛):原理:跑完整个…

qemu模拟单片机

ESP32 乐鑫官方移植的qemu工程:espressif/qemu: Fork of QEMU with Espressif patches. See Wiki for details. 官方使用说明:esp-toolchain-docs/qemu/README.md at main espressif/esp-toolchain-docs 目前支持ES…

RAG-检索增强生成

检索增强生成(Retrieval-Augmented Generation, RAG)是近年来深度学习领域的重要技术突破,旨在解决大语言模型(LLM)在知识更新、事实准确性以及领域专业化方面的局限性。本文将从核心架构、功能实现、实践规范到高…

“猴子补丁”(monkey patch)跟猴子有关吗?

“猴子补丁”(monkey patch)指的是使函数名指向另一个函数。 这个名字里的“猴子”(monkey)和真实的动物猴子没有任何生物学或实体关联,它是编程领域的一个形象比喻,名字的由来与“猴子”的俚语含义有关。 为什么…

Yapi 使用docker在cenos7上部署教程与基本使用

✅ 前提条件 确保你已经完成以下准备:CentOS 7 系统已安装 Docker(未安装请先安装)拥有管理员权限(root 或 sudo)1、安装mongo数据库# 拉取mongo镜像,有本地镜像包可以直接导入 docker pull mongo # 安装mongo数…

C语言vsC++

一、核心区别设计理念C 语言:面向过程(Procedure-Oriented),以函数和数据结构为核心,强调算法和步骤的实现,关注 “怎么做”。 C++:基于 C 语言扩展而来,支持面向对象编程(Object-Oriented,OO),引入类、对…

20251010 之所思 - 人生如梦

20251010 之所思这两天想的事情有点多,有点焦虑,因为刚刚升职,部门的软件需要管理起来,已经不再仅仅管理自己现在的一个小组,今天看了一下软件的人数,数量已经到了122人,这是一个很庞大的队伍,如何管理好,心里…

2025.10.10

今天学生会开会导致十点没有回到宿舍 今天一节英语课,单词背诵情况不好,以后会加强 尝试轻食餐,不好吃

个人书单-从心流出发,学习积极心理学

从“心流”出发,构建你的积极心理学知识体系 | 专题书单 你是否曾有过这样的体验:在全神贯注地工作、运动或创作时,忘记了时间的流逝,内心充满了一种纯净而高效的愉悦感? 这就是米哈里契克森米哈赖所描述的 “心流…

等号(=)在C语言和python中有什么区别?

等号(=)在C语言和python中有什么区别? 等号(=)在 C 语言和 Python 中虽然核心功能都是“赋值”,但由于两种语言的设计理念(静态类型 vs 动态类型、编译型 vs 解释型)存在根本差异,= 的行为、特性和使用场景有…

AI元人文(十四)之价值共生篇:再论物物交换——作为价值共生基础的元协议

AI元人文(十四)之价值共生篇:再论物物交换——作为价值共生基础的元协议 文 / 岐金兰 摘要: 本文是对前文《AI元人文(十四):物物交换——论跨领域价值协商的元协议》提出的“物物交换”元协议的深化探讨。文章将…

4.布局系统

Aquarius 开发框架旨在帮助独立开发者和中小型团队,完成 iOS App 的快速实现与迭代。使用框架开发将给你带来简单、高效、易维护的编程体验。大家好,我是K哥。一名独立开发者,同时也是Swift开发框架【Aquarius】的作…

虚拟环境QA

Dingxingdi: 我使用conda激活虚拟环境之后,就可以像往常一样使用命令了吗?不需要在命令前面加一个conda吗? GitHub Copilot: 可以直接像平常一样运行命令,不需要在命令前加 conda。激活环境(例如 conda activate …

提升高度的网站

真相查询类Snopes https://www.snopes.comWikipedia(英文) https://en.wikipedia.org全球时事类 3. Foreign Affairs https://www.foreignaffairs.comBrookings https://www.brookings.edu课程学习类 5. Open Yale C…

day009

今日完成:css页面的格式排布设置,页面的字体颜色设置有了一定的了解. 明日完成:css,html 遇到问题:无