Python闭包详解:理解闭包与可变类型和不可变类型的关系

一、定义

闭包(Closure) 指的是一个函数对象,即使其外部作用域的变量已经不存在了,仍然能访问这些变量。简单来说,闭包是由函数及其相关的环境变量组成的实体。

def outer():x = 10def inner():print(x)return innerf = outer()
f()  # 输出10

对于这个示例,我们可以将inner函数称为闭包,它满足了嵌套函数,它满足了使用外部函数outer的参数x,外部函数的返回值满足了是inner函数本身

二、闭包

(1)闭包的形成条件

闭包必须是一个嵌套函数,并且内部函数引用了外的变量,外部函数的返回值是该内部函数,对于这个引用外部函数变量的内部函数称为闭包。

  • 必须有嵌套函数(函数内部定义函数)
  • 内部函数引用了外部函数的变量
  • 外部函数返回内部函数对象

示例1:没有嵌套函数,只有单层函数

def func():x = 10print(x)func()

示例2:嵌套函数存在,但内部函数没有引用外部变量

def outer():x = 10def inner():print("Hello")return innerf = outer()
f()

示例3:嵌套函数引用了外部变量,但外部函数没有返回该内部函数

def outer():x = 10def inner():print(x)inner()  # 调用内部函数,但没有返回它outer()

(2)闭包的使用场景

1)数据隐藏和封装:闭包可以把一些变量藏在函数里面,不让外面直接访问,这样就不会乱用或误改,避免弄乱全局变量,让代码更安全。

2)装饰器实现:装饰器本质上依赖闭包机制

3)函数工厂:闭包可以帮你根据不同需求,快速生成带有特定设置或环境的函数,就像工厂按订单生产不同产品一样。

def make_multiplier(factor):# 这是一个函数工厂,传入一个倍数 factordef multiplier(number):# multiplier 是闭包,记住了外面的 factorreturn number * factorreturn multiplier# 生成一个把数字乘以3的函数
times3 = make_multiplier(3)
print(times3(5))  # 输出 15# 生成一个把数字乘以10的函数
times10 = make_multiplier(10)
print(times10(5))  # 输出 50

(3)闭包的作用与优势

1)减少全局变量使用,提升代码安全性

闭包让变量“藏”在函数里,避免把变量放到全局,减少冲突和错误,让代码更可靠。

2)保持函数运行环境,方便管理状态

闭包可以记住外部变量的值,即使外部函数已经结束,内部函数还能继续用这些数据,方便管理和维护程序状态。

三、Python中的可变类型与不可变类型

3.1 变量类型

(1)不可变类型

包括:int、float、str、tuple、frozenset等,对象一旦创建,值不能被改变,任何修改都会生成新对象。

(2)可变类型

包括:list、dict、set、自定义类对象等,对象创建后,内容可以被修改,地址不变。

3.2 两者区别与内存表现

1)不可变类型变量修改时,实际是创建了新的对象,变量指向新地址

a = 10
print(id(a))  # 假设输出:140703079708016a = a + 1
print(id(a))  # 输出:140703079708048 (地址发生变化)

说明:变量 a 原来指向值为10的对象,修改后指向了新创建的值为11的对象,地址发生变化。

2)可变类型变量修改时,变量指向的对象地址不变,内容发生变化

lst = [1, 2, 3]
print(id(lst))  # 假设输出:140703080123456lst.append(4)
print(id(lst))  # 输出:140703080123456 (地址未变)
print(lst)      # 输出:[1, 2, 3, 4]

说明:变量 lst 指向的列表对象地址没有变,但列表内部内容发生了变化。

3)通过id()函数可以观察变量地址变化。id() 返回对象的内存地址标识,可以用来判断变量是否指向同一个对象。

3.3 赋值和修改对变量的影响

1)对不可变类型变量赋值,变量绑定新对象,不影响原对象

2)对可变类型变量修改,直接改变对象内容,所有引用该对象的变量都能感知变化

四、闭包中变量的可变性影响

4.1 闭包对不可变类型变量的访问与限制

闭包内部访问外部不可变变量时,如果尝试修改,会报错(UnboundLocalError),因为修改会被当作局部变量赋值,导致访问冲突。

def outer():x = 10  # 外部不可变变量def inner():x += 5  # 尝试修改外部变量,报错!print(x)inner()outer()

解释:

在 inner 函数里,写了 x += 5,这相当于想给 x 重新赋值。Python 看到这个,就把 x 当成是 inner 里的“新变量”,而不是外面那个已经有值的 x。但是这个“新变量”还没被定义,结果你又想用它来计算,就出错了。

简单说,就是闭包里面如果你想修改外面那个数字,Python 会误以为你是在用一个自己新建但还没给值的变量,所以会报错。要想修改外面的变量,需要告诉 Python “嘿,我用的是外面的那个变量”,这时候就得用 nonlocal。

4.2 使用 nonlocal 关键字修改不可变变量

nonlocal 的作用就是告诉 Python:“我想用的是外面函数里的那个变量,不是新建一个新的。”

所以在 inner 里写了 nonlocal x,Python 就知道你要改的是外面 outer 函数里的 x,然后你给它加 5,修改成功了。这样,inner 里和外面打印的 x 都变成了 15,因为它们指的是同一个变量。

def outer():x = 10def inner():nonlocal xx += 5print(x)inner()print(x)outer()
"""
输出:
15
15
"""
4.3 闭包中对可变类型变量的修改与访问

对可变类型变量,闭包内部可以直接修改其内容,无需nonlocal。当然建议使用nonlocal关键字声明。

def outer():lst = [1, 2, 3]def inner():lst.append(4)print(lst)inner()print(lst)outer()
"""
输出
[1, 2, 3, 4]
[1, 2, 3, 4]"""

为什么修改lst不需要使用nonlocal关键字呢。由于lst是一个列表他是一个可变类型,内层函数在对lst进行添加操作时,并没有改变lst本身,它所指向的内存空间也没有发生改变。如果内层函数代码为del lst 想要删除这个列表,也就是对外层函数的lst做了修改操作,此时就会发生报错UnboundLocalError,如果加上关键字nonlocal进行声明,就可以对它进行删除。

4.4 常见误区

1)不是所有闭包里的变量修改都要用 nonlocal

def outer():lst = [1, 2, 3]  # 可变类型变量def inner():lst.append(4)  # 直接修改列表内容,无需nonlocalprint(lst)inner()print(lst)outer()

2)别把“换变量”和“改内容”搞混了

def outer():x = 10  # 不可变类型lst = [1, 2, 3]  # 可变类型def inner():# x += 1  # 重新绑定,会报错,需要nonlocal# 改成下面这样才合法:nonlocal xx += 1# 对列表内容修改,不是重新绑定lst.append(4)print(x, lst)inner()print(x, lst)outer()

3)别忽略了可变对象改了内容,闭包里的变量也跟着变

def outer():d = {'count': 0}  # 可变字典def inner():d['count'] += 1  # 修改字典内容print(d['count'])return innerf = outer()
f()  # 输出1
f()  # 输出2

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

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

相关文章

BotCash:GPT-5发布观察 工程优化的进步,还是技术突破的瓶颈?

BotCash:GPT-5发布观察 工程优化的进步,还是技术突破的瓶颈? 在GPT-4以多模态能力震撼业界的一年后,GPT-5的亮相显得有些“平静”。当人们期待着又一场颠覆性技术革命时,这场发布会更像是给大模型技术按下了“精细打磨…

AJAX学习(2)

目录 一.XMLHttpRequest 二.XMLHttpRequest——查询参数 三.案例——地区查询 四.XMLHttpRequest_数据提交 五.Promise 六.Promise三种状态 七.PromiseeeXHR获取省份列表(案例) 八.封装-简易axios-获取省份列表 九.封装-简易axios-获取地区列表 …

解决 pip 安装包时出现的 ReadTimeoutError 方法 1: 临时使用镜像源(单次安装)

解决 pip 安装包时出现的 ReadTimeoutError 当您在使用 pip 安装 Python 包时遇到 pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(hostfiles.pythonhosted.org, port443): Read timed out. 错误时,这通常是由于网络问题导致的连接超时。P…

Linux下使用Samba 客户端访问 Samba 服务器的配置(Ubuntu Debian)

在 Linux 系统中,Samba 提供了与 Windows 系统文件共享的便利方式。本文将详细介绍在 Ubuntu 和 Debian 系统下如何安装 Samba 客户端、访问共享资源,并实现远程目录挂载和开机自动挂载。 文章参考自(感谢分享):https…

解决dedecms文章默认关键字太短的问题

在管理文章或软件的时候,大家在添加关键字和内容摘要的时候,是不是对这样的情况感到比较的郁闷,我的关键字设定的明明非常的好,可是添加或修改后,会被无缘无故的截去很多,想必大家也都非常的明白&#xff0…

K8s-kubernetes(二)资源限制-详细介绍

K8s如何合理规定对象资源使用 基本概念 Kubernetes中,占用资源的最小单元为单个PodKubernetes中,资源占用主要针对服务器的CPU、内存 为什么要做资源限制 对于Kubernetes集群而言,所有Pod都会占用K8s集群所在服务器的资源,如果不做…

量子神经网络:从NISQ困境到逻辑比特革命的破局之路

——解析2025千比特时代开发者的机遇与行动框架 引言:量子计算的“20比特魔咒”与千比特悖论 当开发者被建议“避免在>20量子比特电路训练”时,富士通却宣布2025年实现10,000物理比特系统。这一矛盾揭示了量子计算从NISQ时代向FTQC时代跃迁的核心逻辑:千比特突破非为直接…

react+vite-plugin-react-router-generator自动化生成路由

前言:react项目实际使用中有很多提升性能与功能的插件,今天来说一说vite里面提供的vite-plugin-react-router-generator,他主要提供了自动生成路由的功能,配合我们的loadable/component可以实现路由的懒加载与统一管理。1、实现效…

服务器查看 GPU 占用情况的方法

在 Linux 系统中查看 GPU 占用情况,主要取决于你的 GPU 类型(NVIDIA/AMD),以下是常用方法: 一、NVIDIA GPU(最常用,如 RTX 系列、Tesla 系列) 使用 NVIDIA 官方工具 nvidia-smi&…

【Docker实战进阶】Docker 实战命令大全

Docker 实战命令大全 Docker 实战场景,以 Nginx 为核心示例,梳理容器生命周期、镜像管理、网络配置、数据持久化及 Compose 编排的核心命令与最佳实践。 一、容器生命周期管理 1. 基础生命周期命令 docker run - 创建并启动容器 核心功能:基于…

PyCharm 2025.2:面向工程师的 AI 工具

引言 随着人工智能技术的快速发展,AI 工程师对开发工具的需求也在不断提升。PyCharm 2025.2 版本带来了革命性的 AI 工具包,将 AI 开发所需的实验、调试、评估和部署功能原生集成到 IDE 中。这一重大更新不仅提升了开发效率,也为 AI 工程师提…

爬虫逆向--Day15--核心逆向案例2(Python逆向实现请求加密、请求堆栈、拦截器关键字)

一、逆向案例之Python逆向实现请求加密//具体代码如下 function l(t, e) {return t.toString().toUpperCase() > e.toString().toUpperCase() ? 1 : t.toString().toUpperCase() e.toString().toUpperCase() ? 0 : -1}function u(t) {for (var e Object.keys(t).sort(l)…

时序数据库市场前景分析

1. 引言随着物联网(IoT)、工业互联网、金融科技、智慧城市等领域的快速发展,数据呈现爆发式增长,其中时间序列数据(Time-Series Data)占据了重要地位。时序数据库(Time-Series Database, TSDB&a…

【网络安全测试】Burp Suite使用指导、配置及常见问题介绍(有关必回)

Burp Suite 是**渗透测试领域事实上的标准工具**,尤其擅长Web应用与API安全测试。针对AI系统,它主要用于测试模型API、管理后台等Web接口。以下是专业级使用指南:---### **一、 核心模块与功能概览**| **模块** | **核心功能** | **AI测试重点…

iOS 26 一键登录失效:三大运营商 SDK 无法正常获取手机号

近期,不少开发者和用户反馈,在升级到 iOS 26 系统后,App 内的 一键登录功能无法正常使用。无论是移动、电信还是联通的 SDK,都会出现无法获取手机号的情况,导致用户需要改用短信验证码或手动输入手机号完成登录。问题现…

OpenLayers与Vue.js结合实现前端地图应用

OpenLayers与Vue.js结合实现前端地图应用 下面我将为您展示如何将OpenLayers与Vue.js结合创建一个功能丰富的前端地图应用。这个教程包含了基础地图展示、标记点、地图控件以及交互功能。 实现结果 实现思路 在Vue项目中集成OpenLayers库创建基础地图视图和OSM图层添加标记点…

VisDrone数据集,专为无人机视觉任务打造

在农业巡查、环保监测、安防布控等广阔天地,无人机(UAV)早已超越了“拍照打卡”的酷炫标签,成为不可或缺的智能之眼。然而,当计算机视觉模型从地面“抬头”望向无人机视角时,迎接它的却是截然不同的挑战&am…

【Python】Python 函数基本介绍(详细版)​

Python 函数基本介绍(详细版)​ 文章目录Python 函数基本介绍(详细版)​前言一、函数的创建​1.1 函数名的命名规则​1.2 函数的创建​1.3 函数的调用​二、函数的参数​2.1 形参和实参​2.2 位置参数​2.3 关键字参数​2.4 默认参…

【前端Vue】log-viewer组件的使用技巧

目录 修改行号和组件的样式 修改高亮显示的内容和颜色 **log-viewer组件合集** 【前端Vue】如何优雅地展示带行号的日志文件或文本内容(log-viewer组件的使用) 【前端Vue】使用log-viewer组件时的踩坑记录 【前端Vue】log-viewer组件的使用技巧 【前…

OpenCV Python——报错AttributeError: module ‘cv2‘ has no attribute ‘bgsegm‘,解决办法

Python在使用 bgsubmog cv2.bgsegm.createBackgroundSubtractorMOG() 去除背景,报错AttributeError: module ‘cv2‘ has no attribute ‘bgsegm‘ 报错原因:使用的python环境中没有安装扩展包contrib 可以通过pip或者conda安装 pip install opencv-con…