【Python】 闭包

什么是闭包

用一句话粗略概况为:在一个函数内,读取外部函数定义的变量的机制。更一般地说,闭包函数是带有状态的函数,状态是指调用环境的上下文,当函数带上了状态就是闭包。

如下代码,在函数f内定义了一个嵌套的函数innerinner内往f函数内定义的data中追加元素,可以称inner内使用data的机制为闭包,由于inner是一个函数,只有调用时才会真正执行,固f 一般将inner作为返回值,或者更一般地称函数f返回了一个闭包。

def f():
	data = [] # 闭包函数外的函数中
	
	def inner(value): # 局部作用域
		data.append(value) 
		return data
	
	return inner

g = f()
print(g(1)) # 输出[1]
print(g(2)) # 输出[1, 2]

闭包是如何实现的

作用域回顾

python一共有四种作用域,分别是:L (Local) 局部作用域;E (Enclosing) 闭包函数外的函数中;G (Global) 全局作用域;B (Built-in) 内建作用域,规则顺序: L –> E –> G –> B。在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。在inner函数中查找data时,会先在局部作用域寻找,而局部作用域没有data的定义,转而到闭包函数内找到data的定义,故可以确定,innner内使用的data是在外部函数定义的。

x = int(2.9)  # 内建作用域,查找int函数
data = 0  # 全局作用域

def f():
	data = [] # 闭包函数外的函数中
	
	def inner(value): # 局部作用域
		data.append(value) 
		return data
	
	return inner

内部函数使用外部函数的data时,是拷贝还是引用?

Q:inner函数内引用的data是函数f中定义data的copy?还是finner引用的是同一个data变量?

A:首先给出正面的回答,内层函数与外层函数引用的是同一个变量,可以作以下实验,在f定义inner后,修改data(重新赋值),仍然会影响到inner内的data

def f():
    data = []  # 闭包函数外的函数中
    
    def inner(value):  # 局部作用域
        data.append(value)
        return data

    data = [1] # 在inner后修改data
    return inner

g = f()
print(g(2)) # 输出[1, 2]
print(g(3)) # 输出[1, 2, 3]

闭包变量的值保存在哪?

如果简单把data理解为f的局部变量,局部变量保存在栈上,data只会随f的调用构建,f结束时销毁。但事实远非如此,首先data并不是简单的局部变量,data在调用一次f函数后,甚至对函数f的引用都没有了,data仍然可以被inner函数访问。由于Python为引用计数机制,因此一定存在对象保持对data的引用,那么是谁引用着data

实际上data是被保存在Cell Objects里(Cell引用data),以下是Cell Object官方的解释。

  • “Cell” objects are used to implement variables referenced by multiple scopes. For each such variable, a cell object is created to store the value; the local variables of each stack frame that references the value contains a reference to the cells from outer scopes which also use that variable. When the value is accessed, the value contained in the cell is used instead of the cell object itself.

“Cell”对象用于实现由多个作用域引用的变量。对于每个这样的变量,都会创建一个Cell对象来存储值;引用该变量的局部变量都包含对外部作用域中也使用该变量的Cell对象的引用。访问该值时,将使用单元格中包含的值,而不是单元格对象本身。单元格对象的这种取消引用需要生成的字节码的支持;访问时不会自动取消引用。单元格对象在其他地方可能没用。

Cell引用data哪谁引用Cell?Cell活在哪(保存在哪)?实际上在调用f时,会构建function Object inner,由于使用了闭包变量data,在构建inner时,会设置function object的闭包变量__closure__ ,这个闭包变量__closure__引用着Cell Object,而Cell引用着data,如下示例。

def f():
    data = []  # 闭包函数外的函数中

    def inner(value):  # 局部作用域
        data.append(value)
        return data

    data = [1]
    return inner

enclosure = f()
print(enclosure.__closure__) #(<cell at 0x7f7642bebbe0: list object at 0x7f7642aa3c40>,)
print(enclosure(2))          # [1, 2]
print(hex(id(enclosure(3)))) # 0x7f7642aa3c40

在这里插入图片描述

总结

**闭包允许函数定义自己函数内的“全局”变量,**做到长久保存而不污染全局命名空间,即如在f函数内定义新的函数时,f内新定义的函数可以像使用全局变量一样使用f函数内定义的变量。

闭包的核心作用是建立一个独立的scope,做到变量隔离,装饰器是闭包的一大应用

后记

注:闭包不是Python特有的概念,而是广泛出现在各种编程语言中,如C++ lambda也可以实现闭包。C++中的通过lambda捕获闭包变量时,不仅可以进行引用捕获,还可以进行值捕获,Python的闭包默认为**[&](){}形式的引用捕获**。

int main() {
    int round = 2;
    auto f = [=](int f) -> int { return f + round; } ;
    cout << "result = " << f(1) << endl;
    return 0;
}

参考资料

https://www.liujiangblog.com/course/python/32

https://www.bilibili.com/video/BV1Ab4y1P7ew/?spm_id_from=333.788&vd_source=36208a91e67eaf84fb870cccb7dead12

https://en.cppreference.com/w/cpp/language/lambda

https://zhuanlan.zhihu.com/p/453787908

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/691210.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pyinstall 打包 paddleocr 成为.exe文件步骤

一、首先进入虚拟环境 使用pip安装pyinstaller pip install pyinstaller我的已经安装完成 二、用cmd进入当前打包文件夹下&#xff0c;新建使spec文件内容如下 注意&#xff1a;其中需要修改的部分是pathex中文件所在路径文件内容摘抄自另一篇博文(❄点击可查看❄) # -*- m…

pytorch 加权CE_loss实现(语义分割中的类不平衡使用)

加权CE_loss和BCE_loss稍有不同 1.标签为long类型&#xff0c;BCE标签为float类型 2.当reduction为mean时计算每个像素点的损失的平均&#xff0c;BCE除以像素数得到平均值&#xff0c;CE除以像素对应的权重之和得到平均值。 参数配置torch.nn.CrossEntropyLoss(weightNone,…

2024 cicsn ezbuf

文章目录 参考protobuf逆向学习复原结构思路exp 参考 https://www.y4ng.cn/posts/pwn/protobuf/#ciscn-2024-ezbuf protobuf 当时压根不知道用了protobuf这个玩意&#xff0c;提取工具也没提取出来&#xff0c;还是做题做太少了&#xff0c;很多关键性的结构都没看出来是pro…

Vue的基础知识:v-model的原理,由:value与@input合写。

原理&#xff1a;v-model本质上是一个语法糖&#xff0c;比如应用在输入框上&#xff0c;就是value属性和input事件的合写。&#xff08;补充说明&#xff1a;语法糖就是语法的简写&#xff09; 作用&#xff1a;提供数据的双向绑定 1.数据变&#xff0c;视图&#xff08;也就…

spring-kafka-生产者服务搭建测试(SpringBoot整合Kafka)

文章目录 1、生产者服务搭建1.1、引入spring-kafka依赖1.2、application.yml配置----v1版1.3、使用Java代码创建主题分区副本1.4、发送消息 1、生产者服务搭建 1.1、引入spring-kafka依赖 <?xml version"1.0" encoding"UTF-8"?> <project xml…

王学岗鸿蒙开发(北向)——————(七)ArkUi的各种装饰器

arts包含如下&#xff1a;1&#xff0c;装饰器 &#xff1b;2&#xff0c;组件的描述(build函数)&#xff1b;3&#xff0c;自定义组件(Component修饰的),是可复用的单元&#xff1b;4&#xff0c;系统的组件(鸿蒙官方提供)&#xff1b;等 装饰器的作用:装饰类、变量、方法、结…

【Qt】Frame和Widget的区别

1. 这两个伙计有啥区别&#xff1f; 2. 区别 2.1 Frame继承自Widget&#xff0c;多了一些专有的功能 Frame Widget 2.2 Frame可以设置边框

【Python列表解锁】:掌握序列精髓,驾驭动态数据集合

文章目录 &#x1f680;一、列表&#x1f308;二、常规操作&#x1f4a5;增&#x1f4a5;删&#x1f4a5;改&#x1f4a5;查 ⭐三、补充操作 &#x1f680;一、列表 列表是一个能够存储多个同一或不同元素的序列 列表&#xff1a;list ---- [] 列表属于序列类型&#xff08;容器…

Unity2D游戏制作入门 | 09(之人物动画制作)

上期链接&#xff1a;Unity2D游戏制作入门 | 08-CSDN博客 人物走路动画逻辑补充&#xff08;该帖没有的内容&#xff0c;我给补充了请先看完这帖&#xff0c;再去看补充&#xff09;&#xff1a;人物按下shifit走路动画设定09&#xff08;第九期先行补充&#xff09; 上期我们…

汇编语言(keil)

1、读内存&#xff1a;Load LDR R0,[R1,#4]; 读地址“R14”&#xff0c;得到的4字节数据存入R0。 2、写内存&#xff1a;Stroe STR R0,[R1,#4]; 把R0的4字节数据写入地址“R14”。 3、加减 ADD R0,R1,R2; R0R1R2 ADD R0,R0,#1; R0R01 SUB R0,R1,R2; R0R1-R…

GiantPandaCV | 提升分类模型acc(一):BatchSizeLARS

本文来源公众号“GiantPandaCV”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;提升分类模型acc(一)&#xff1a;BatchSize&LARS 在使用大的bs训练情况下&#xff0c;会对精度有一定程度的损失&#xff0c;本文探讨了训练的b…

【CS.SE】端午节特辑:Docker容器化技术详解与实战

端午节, 先祝愿大家端午安康&#xff0c;阖家幸福, 哈哈&#xff01;这篇讲下Docker这一现代软件开发中不可或缺的技术。软件工程涉及软件开发的整个生命周期&#xff0c;包括需求分析、设计、构建、测试、部署和维护。Docker作为一种容器化技术&#xff0c;直接关联到软件部署…

WWDC 2024前瞻:苹果如何用AI技术重塑iOS 18和Siri

苹果下周的全球开发者大会有望成为这家 iPhone 制造商历史上的关键时刻。在 WWDC 上&#xff0c;这家库比蒂诺科技巨头将展示如何选择将人工智能技术集成到其设备和软件中&#xff0c;包括通过与 OpenAI 的历史性合作伙伴关系。随着重大事件的临近&#xff0c;有关 iOS 18 及其…

uniapp引入uview无代码提示

前提安装正确&#xff1a; 无论是基于npm和Hbuilder X方式安装&#xff0c;一定要配置正确。 解决办法 以前在pages.json里面的写法&#xff1a; "easycom": {"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue" }但是现在hbuilderx要求规范ea…

【小白专用24.6.8】C# 异步任务Task和异步方法async/await详解

一、什么是异步 同步和异步主要用于修饰方法。当一个方法被调用时&#xff0c;调用者需要等待该方法执行完毕并返回才能继续执行&#xff0c;我们称这个方法是同步方法&#xff1b;当一个方法被调用时立即返回&#xff0c;并获取一个线程执行该方法内部的业务&#xff0c;调用…

springboot+minio+kkfileview实现文件的在线预览

在原来的文章中已经讲述过springbootminio的开发过程&#xff0c;这里不做讲述。 原文章地址&#xff1a; https://blog.csdn.net/qq_39990869/article/details/131598884?spm1001.2014.3001.5501 如果你的项目只是需要在线预览图片或者视频那么可以使用minio自己的预览地址进…

C++期末复习总结(2)

目录 1.运算符重载 2.四种运算符重载 &#xff08;1&#xff09;关系运算符的重载 &#xff08;2&#xff09; 左移运算符的重载 &#xff08;3&#xff09;下标运算符的重载 &#xff08;4&#xff09;赋值运算符的重载 3.继承的方式 4.继承的对象模型 5.基类的构造 6…

中缀表达式和前缀后缀

在中缀表达式中&#xff0c;操作数可能与两个操作符相结合 但是&#xff0c;想要不带括号无歧义&#xff0c;且不需要考虑运算符优先级和结合性 所以考虑 前缀表达式&#xff0c;波兰表达式 后缀表达式 逆波兰表达式 对于人来说&#xff0c;中缀表达式是最容易读懂的。但是对于…

C语言之字符函数总结(全部!),一篇记住所有的字符函数

前言 还在担心关于字符的库函数记不住吗&#xff1f;不用担心&#xff0c;这篇文章将为你全面整理所有的字符函数的用法。不用记忆&#xff0c;一次看完&#xff0c;随查随用。用多了自然就记住了 字符分类函数和字符转换函数 C语言中有一系列的函数是专门做字符分类和字符转换…

无人机、机器人10公里WiFi远距离图传模块,实时高清视频传输,飞睿CV5200模组方案,支持mesh自组网模块

在快速发展的物联网时代&#xff0c;远距离无线通信技术已成为连接各种智能设备的关键。无人机、安防监控、机器人等领域对数据传输的距离和速度要求越来越高。 公里级远距离WiFi模组方案可以通过多种技术和策略的结合来实现无人机和机器人之间的高效通信传输。 飞睿智能CV52…