【Python】 闭包

news2024/11/29 8:46:43

什么是闭包

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

如下代码,在函数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.coloradmin.cn/o/1804502.html

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

相关文章

IT闲谈-Kylin入门教程

目录 一、引言二、Kylin简介三、环境准备四、安装与配置五、数据导入与建模六、查询与分析七、总结 一、引言 Apache Kylin是一个开源的分布式分析引擎&#xff0c;旨在提供Hadoop/Spark之上的SQL接口及多维分析&#xff08;OLAP&#xff09;能力以支持超大规模数据。Kylin通过…

折腾日记:废物利用改造kindle为天气预报日历

个人博客地址 很早之前购买了kindle用来看电纸书&#xff0c;后面也逐渐吃灰了&#xff0c;尤其当去年kndle电子书店停止服务后&#xff0c;就一直没充过电了&#xff0c;中途有段时间想着&#xff0c;当时买的时候还挺贵&#xff0c;不能这么浪费了&#xff0c;给它充满电后&a…

MathType7.8永久破解版下载 让数学学习变得简单有趣!

大家好&#xff0c;我是科技评论家。今天给大家推荐一款非常实用的数学公式编辑器——MathType 7.8&#xff01;&#x1f4f1;&#x1f4b0; 在数字化时代&#xff0c;学术研究、教学和科研领域中的数学公式编辑需求越来越高。而MathType 7.8作为一个广受欢迎的数学公式编辑器&…

DBeaver连接MySQL提示“Public Key Retrieval is not allowed“问题的解决方式

问题描述 客户端root用户连接数据库出现出现Public Key Retrieval is not allowed 原因分析&#xff1a; 加上allowPublicKeyRetrievalfalse&#xff1a; 解决方案&#xff1a; allowPublicKeyRetrievaltrue&#xff1a;

【面试干货】 Hash 索引和 B+树索引的区别

【面试干货】 Hash 索引和 B树索引的区别 1、Hash 索引2、B 树索引3、区别和适用场景 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在数据库中&#xff0c;索引是一种重要的数据结构&#xff0c;用于加速查询操作。常见的索引包括 Hash 索…

tkinter颜色选择器

tkinter颜色选择器 颜色选择器效果代码 颜色选择器 Tkinter 提供了一个简单易用的颜色选择器模块 colorchooser&#xff0c;通过调用 colorchooser.askcolor() 方法&#xff0c;我们可以轻松实现颜色选择功能。 效果 代码 import tkinter as tk from tkinter import colorch…

智慧社区整体解决方案

1.智慧社区整体建设方案内容 2.整体功能介绍

【NOI-题解】1468. 小鱼的航程1074 - 小青蛙回来了1261. 韩信点兵1254. 求车速1265. 爱因斯坦的数学题

文章目录 一、前言二、问题问题&#xff1a;1468. 小鱼的航程问题&#xff1a;1074 - 小青蛙回来了问题&#xff1a;1261. 韩信点兵问题&#xff1a;1254. 求车速问题&#xff1a;1265. 爱因斯坦的数学题 三、感谢 一、前言 本节主要对循环中需要流程控制的题目进行讲解&#…

Numba 的 CUDA 示例(4/4):原子和互斥

本教程为 Numba CUDA 示例 第 4 部分。 本系列第 4 部分总结了使用 Python 从头开始学习 CUDA 编程的旅程 介绍 在本系列的前三部分&#xff08;第 1 部分&#xff0c;第 2 部分&#xff0c;第 3 部分&#xff09;中&#xff0c;我们介绍了 CUDA 开发的大部分基础知识&#xf…

C语言王国——数据的内存管理

目录 一、引言 二、整形在内存中的存储 2.1 进制之间的转换 2.1.1 整形的二进制 2.1.2 十进制和二进制 2.1.3 十进制和八进制的转换 2.1.4 十六进制和十进制的转换 2.2 原码&#xff0c;反码&#xff0c;和补码 三、大、小端字节序 3.1 大小端的定义 3.2 为什么会有大…

LabVIEW FPGA开发NI sbRIO-9607高精度数字滤波器

使用NI sbRIO-9607硬件平台&#xff0c;通过LabVIEW FPGA模块实现一个高精度数字滤波器。该应用不需要额外的实时操作系统 (RT)&#xff0c;所有控制与数据处理均在sbRIO-9607的FPGA上完成&#xff0c;充分利用其并行处理能力&#xff0c;实现低延迟、高性能的数据滤波。这种滤…

【Git】Git 的基本操作 -- 详解

一、创建 Git 本地仓库 要提前说的是&#xff0c;仓库是进行版本控制的一个文件目录。我们要想对文件进行版本控制&#xff0c;就必须先创建一个仓库出来。 创建⼀个 Git 本地仓库对应的命令为 git init &#xff0c;注意命令要在文件目录下执行&#xff0c;例如&#xff1a;…

IT闲谈-IMD是什么,有什么优势

目录 一、引言二、IDM是什么&#xff1f;三、IDM的优势1. 高速下载2. 稳定性强3. 强大的任务管理4. 视频下载5. 浏览器整合 四、应用场景1. 商务办公2. 教育学习3. 娱乐休闲 总结 一、引言 在数字化时代&#xff0c;下载管理器已成为我们日常工作和生活中不可或缺的工具。而在…

Rethinking overlooked aspects in vision-language models

探讨多模态视觉语言模型的一些有趣结论欢迎关注 CVHub!https://mp.weixin.qq.com/s/zouNu-g-33_7JoX3Uscxtw1.Introduction 多模态模型架构上的变化不大,数据的差距比较大,输入分辨率和输入llm的视觉token大小是比较关键的,适配器,VIT和语言模型则不是那么关键。InternVL-…

SpringCloud-远程调用OpenFeign-基本使用

目录 1 直接使用RestTemplate发起Http请求 1.1 将RestTemplate注册为SpringBean 1.2 在service实现类中注入RestTemplate 1.3 使用注入的RestTemplate 传入参数后发起http请求 2 引入Nacos后使用RestTemplate发起Http请求 2.1 基础知识 2.2 Nacos的使用 2.2.1 引入nac…

pytorch 自定义学习率更新 Poly

Poly 学习率调整策略需要继承_LRScheduler类&#xff0c;该类包含三个重要属性和两个重要方法 学习率与batch-size的关系 一般来说&#xff0c;batch-size的大小一般与学习率的大小成正比。batch-size越大一般意味着算法收敛方向的置信度越大&#xff0c;也可以选择较大的学…

四川赤橙宏海商务信息咨询有限公司引领抖音电商新纪元

在数字化浪潮席卷全球的今天&#xff0c;电商行业正以前所未有的速度蓬勃发展。作为电商领域的一匹黑马&#xff0c;四川赤橙宏海商务信息咨询有限公司凭借其专业的服务理念和创新的运营策略&#xff0c;在抖音电商领域崭露头角&#xff0c;成为行业内的佼佼者。 赤橙宏海商务…

coap:安装libcoap

# 称最新版的openssl 安装依赖东东 yum install -y libtool doxygen asciidoc perl-IPC-Cmd下载解压 cd /chz/install/openssl wget https://www.openssl.org/source/openssl-3.3.1.tar.gz tar zxvf openssl-3.3.1.tar.gz编译安装 cd /chz/install/openssl/openssl-3.3.1 .…

关于焊点检测(SJ-BIST)模块实现

关于焊点检测&#xff08;SJ-BIST&#xff09;模块实现 语言 &#xff1a;Verilg HDL 、VHDL EDA工具&#xff1a;ISE、Vivado、Quartus II 关于焊点检测&#xff08;SJ-BIST&#xff09;模块实现一、引言二、焊点检测功能的实现方法&#xff08;1&#xff09; 输入接口&#x…

服务部署:Linux系统部署C# .NET项目

1. 安装 .NET SDK 首先&#xff0c;你需要在你的 Linux 系统上安装 .NET SDK。 Ubuntu系统&#xff1a; 下载 Microsoft 包配置文件 wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb 这个命令使用 wge…