python学习之【with语句】

news2025/7/9 14:28:03

前言

上一篇文章 ​ ​python学习之【文件读写】​​​ 中我们学习了python当中的文件读写,这篇文章接着学习python中文件读写的with语句

了解with语句

在很多场景中,通过使用with语句可以让我们可以更好地来管理资源和简化代码,它可以看做是对

try…finally模式的简化。

with语句的语法格式
with open(文件名,打开方式,编码格式) as 别名:
with语句体

with语句可以自动管理上下文资源,不论是什么原因跳出with块都能确保文件可以正确的关闭 ,以此来达到释放资源的目的。

举几个例子:

如果是利用以前的知识来进行文件读取的操作,流程就是打开文件——》读取文件——》关闭文件。
例如:

# 比如我们要读取一个文件,如果不用with语句实现的话:
#打开文件
file_a=open('a.txt','r')
#读取文件
print(file_a.read())
#关闭文件,释放资源
file_a.close()

但是假如在读取文件的过程中出现异常的话,会对我们后续的关闭文件操作产生影响,从而导致资源无法释放。那应该怎样实现在读取文件时无论有没有产生异常都让打开了的文件及时关闭呢?我们可能会想到用try…except去完成

try:
    # 打开文件
    reader=open('a.txt','r')
    # 操作文件
    content=reader.read()
    print(content)
except Exception: #有异常打印出异常,避免程序性报错
    print(Exception)
finally:
    #关闭文件
    reader.close()

try…except 的确会当对文件操作时发生异常不让程序发生报错,但是try…except是一种固定的格式,如果我们需要读取的文件有十几二十个呢,是不是需要按照try…except 的语法格式写很多条关闭操作的固定代码呀,这时候就出现了with语句来简化我们繁琐的代码:

#用with语句写 执行完毕with语句体之后会自动调用open中的__exit__方法对文件关闭,
#我们就不需要再写close()操作了
with open('a.txt','r') as file:
    print(file.read())

我们看只需要两行代码就能实现文件的读写;下面这个例子会更直观的看出with语句的妙处:

# 不使用with语句复制图片
file_src=open('befor.png','rb')
file_tgt=open('after.png','wb')
file_tgt.write(file_src.read())
file_src.close()
file_tgt.close()

# 使用with语句复制图片
with open('befor.png','rb') as src_file:
    with open('copy.png','wb') as tgt_file:
        tgt_file.write(src_file.read())

我们能够看出使用with语句不仅简化了我们的代码书写,更避免了我们在读取文件完毕后忘记写入关闭文件的操作,增强了文件的安全性。

实现原理

上文我们提到了上下文管理器,其实with语句就是通过上下文管理器来实现对文件的打开关闭操作。

当一个类中包含__enter__ 方法 和 __exit__方法时,我们就可以称这个类为上下文管理器的类。

使用with执行这个类时,会自动调用类的__enter__方法;然后执行with 语句体的内容;

当with语句体的内容执行完毕后,最后再调用执行类中的__exit__方法 实现关闭文件的操作。

我们先用代码实现with语句的工作原理,以便让我们更熟练的使用with语句:

#定义一个类叫做MyContent
class  MyContent():
    # 特殊方法
    def __enter__(self):
        print('enter方法被调用了')
        return self
    # 特殊方法
    def __exit__(self,exc_type,exc_val,exc_tb):
        print('exit方法被调用了')
    # 实例方法
    def show(self):
        print('实例方法show()被调用了')
with MyContent() as file:
    file.show()  
    #离开运行时上下文(with语句体执行完毕),自动调用上下文管理器的特殊方法__exit__()

运行结果如下:
在这里插入图片描述
这个例子中,MyContent类就是一个上下文管理器的类,这个类中包含__enter__ 方法 和 exit__方法,当执行到with语句时,会自动调用该类中的__enter__方法;然后就开始执行with语句体,with语句体中我们通过类的对象调用了show()方法,因此show()方法被执行;最后当with语句体全部执行完毕后,就开始调用类中的__exit__方法让文件得以关闭。

由此,前文用with语句实现对文件读取的操作的工作原理也就显而易见了:

with open('a.txt','r') as file:
    print(file.read())

with语句后紧跟的open其实就是一个含有__enter__ 方法 和 exit__方法的类,因此这个类就叫做上下文管理器的类。

​我们可以调用python中定义好的open类,也可以自定义一个类,让它也具有open类的功能:

自定义一个类实现with语句

class  File_Manager(object):
    #定义一个初始化方法
    def __init__(self,name,mode):
        print('调用了__init__方法')
        # 实例化对象
        self.name=name
        self.mode=mode
        self.file=None
    # 定义两个特殊方法  __enter__和__exit__方法

    def __enter__(self):
        print('调用了__enter__方法')
        # 将打开的文件名和打开文件的方式赋给self.file
        self.file=open(self.name,self.mode)
        return self.file

    def __exit__(self,exc_type,exc_val,exc_tb):
        print('调用了exit方法')
        # 如果指定文件被打开,就执行close()操作
        if self.file:
            self.file.close()

    # 定义一个实例方法
    def show(self):
        print('show()被调用',self)

#使用with语句,这里的上下文管理器对象是我们创建的 File_Manager    我们在创建该类时对变量name和mode进行实例化了,因此这里需要给该类传递两个实例化对象 
#文件名和 打开方式

with File_Manager('a.txt','r') as read_file:
    content=read_file.read()
    print('读取a.txt',content)
#调用实例方法show()
    read_file=File_Manager('a.txt','r')
    read_file.show()

运行结果:

在这里插入图片描述

我们定义了一个File_Manager()类,在这个类中定义了两个特殊方法__enter__() 方法和__exit__()方法,这两个方法分别负责对文件的开始和对文件的关闭;在执行with语句时,我们用自定义的File_Manager()类来替换原本的with语句中调用的open类,当执行with语句时,File_Manager()类就被调用,从而开始执行类中的__enter__() 方法和__exit__()方法以达到文件开启关闭的作用。

with语句读取文件运行流程:

1 当我们在进行实例化对象的时候,__init__初始化方法会自动被调用 ——》调用了__init__方法
2 当执行with语句的时候,会自动调用类中的__enter__方法 ——》调用了__enter__方法
3 当执行到__enter__方法的return sele.file 时,会返回一个文件对象,此时开始读取文件——》读取a.txt ——》hello world
4 当文件读取完毕时最后执行__exit__方法 ——》调用了exit方法

我们可以看到,当我们在with语句体中使用类的对象来调用类中的实例方法时,依然是先执行调用的

show()方法,然后再执行__exit__方法;

也就是说只有当with语句体的内容全部执行完毕后,才会执行__exit__方法关闭文件,释放资源

有关异常处理

上文我们说到with语句可以看作是对try…expect模式的简化,那么如果我们使用with语句来对文件进行操作时,with语句体中发生异常,会不会导致程序报错影响文件的关闭操作呢?

class  MyContent():
    # 特殊方法
    def __enter__(self):
        print('enter方法被调用了')
        return self
    # 特殊方法
    def __exit__(self,exc_type,exc_val,exc_tb):
        print('exit方法被调用了')
    # 实例方法
    def show(self):
        print('实例方法show()被调用了')
        # return 1/0  # 即使是实例方法中出现了异常,特殊方法__exit__都会被调用,使得文件关闭
with MyContent() as file:
    a=1/0
    file.show()  #离开运行时上下文,自动调用上下文管理器的特殊方法__exit__()

我们在with语句体中写入了报错代码:a=1/0,这就相当于with语句体发生异常了,那么运行结果时怎样的呢:

在这里插入图片描述

从运行结果可以看出,即使在with语句体中出现异常,但是不会影响对__exit__()方法的调用,依然会对文件实行关闭操作。

但是虽然在发生异常时并没有影响文件的关闭,但是无论怎样程序还是报错了,那么with语句可不可以像try…expect模板那样将错误打印出来,避免程序的报错呢?我们不妨尝试一下:

在这里插入图片描述
在这里插入图片描述
我们看到python中根本就没有with…expect和with…else的语法,确实写不下去。

那我们是不是可以将try…expect和with联用呢,也就是将with语句作为一个块,然后嵌套在try…expect模块的try中:

# 尝试将with语句嵌套在try...expect
try:
    with open('a.txt','r') as read_file:
        a=1/0
        print(read_file.read())
except Exception: #有异常打印出异常
    print(Exception)
finally:
    #关闭文件
    read_file.close()

在这里插入图片描述
ok问题解决!

优化代码,将错误原因打印出来:

try:
    with open("a.txt") as f:
            a=1/0
            print(f.readlines())
except Exception as error:
    print(error)
finally:
    f.close()

在这里插入图片描述

#用错误的方法读取二进制文件  
try:
    with open('pic1.png') as p:
        print(p.read())
except Exception as error:
        print(error)
finally:
    print('文件已关闭')
    p.close()

在这里插入图片描述
由此可知:with语句只能确保对文件操作时是否发生异常都可以及时关闭打开的文件;如果出现异常,程序依然会发生报错。

为什么会出现这种情况?

我们注意到在定义__exit__()这个特殊方法时,传入了三个参数:

__exit__(exc_type, exc_val, exc_tb): 

这三个参数分别代表:异常类型 (exc_type)、值 (exc_value) 及回溯信息 (traceback)

退出与上下文管理器相关的运行时上下文,返回一个bool 值表示是否对发生的异常进行处理。

参数为引起退出操作的异常信息,如果退出时未发生异常,则3个参数均为None。

若发生异常,返回 True 表示无需处理异常,返回 False 则会在退出该方法后重新抛出异常以由 with 语句之外的代码逻辑进行处理。

若该方法内部产生异常,则会取代由 statement-body 中语句产生的异常。要处理异常时,不应显示重新抛出异常
(即不能重新抛出通过参数传递进来的异常),只需将返回值设为 False 即可。之后,上下文管理代码会检测是否 exit()
失败来处理异常。

简单来说:

一个上下文管理器的__exit__方法,如果它返回False将在错误结束时重报错误。如果它返回True,它将抑制它。但是open内置的__exit__不返回True,因此当with语句体中执行出异常时,执行了__exit__方法关闭文件,错误还是会被重报,并不会避免。

由此可得当with语句体出现异常时,with语句采取的措施是:

1:将发生异常的类型 (exc_type)、值 (exc_value) 及回溯信息 (traceback) 作为实参传递给 exit
方法;

2:使得 exit 方法处理异常;

3:如果__exit__ 方法返回 True,则说明该异常已被“处理”,无需再处理;

4:如果__exit__ 方法返回 True 之外的任何内容,则该异常将被 with 语句抛出;

说到这里,我们是不是可以通过对__exit__()功能的完善来处理异常发生呢?

class File(object):
    def __init__(self, name, mode):
        self.file = open(name, mode)
    def __enter__(self):
        return self.file
    def __exit__(self, type, value, traceback):
        print("异常已捕获")  
        self.file.close()  # 关闭文件对象
        print("文件已关闭")  
        return True  # 返回 True 说明异常已被处理

with File('a.txt', 'r') as file:
    #触发异常
    a=1/0
    file.read()

在这里插入图片描述

每篇一语

一辈子,别错把放纵当潇洒,别把颓废当自由。

如有不足,感谢指正!

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

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

相关文章

洛谷刷题入门篇:顺序结构

链接如下:https://www.luogu.com.cn/training/100#problems 一、Hello,World! 题目链接:https://www.luogu.com.cn/problem/B2002 题目描述 编写一个能够输出 Hello,World! 的程序。 提示: 使用英文标点符号;Hello,World! 逗…

Windows下,快速部署开发环境,第三方库管理,以及项目迁移工具介绍

对于在windows下做c开发的同学,你是否有以下痛点?: 1.每次构建c项目,搭配第三方库环境,都要不停的include,lib,dll等配置,如果4-5个还好,要是10几个...人都麻了... 2.一个环境也无所谓,问题x64/32位系统,Debug,Release都要配置一遍..每次配置…

【C# Programming】值类型、良构类型

值类型 1、值类型 值类型的变量直接包含值。换言之, 变量引用的位置就是值内存中实际存储的位置。 2、引用类型 引用类型的变量存储的是对一个对象实例的引用(通常为内存地址)。 复制引用类型的值时,复制的只是引用。这个引用非常小&#xf…

CentOS安装openjdk和elasticsearch

CentOS安装openjdk 文章目录 CentOS安装openjdk一、yum1.1search1.2安装openjdk 二、elasticsearch的启动和关闭2.1启动2.2关闭2.3添加服务 一、yum 1.1search yum search java | grep jdk1.2安装openjdk [roottest ~]# yum install java-1.8.0-openjdk -y 查看openjdk版本 …

校园学习《乡村振兴战略下传统村落文化旅游设计》 许少辉瑞博士生辉少许

校园学习《乡村振兴战略下传统村落文化旅游设计》 许少辉瑞博士生辉少许

无线定位中TDOA时延估计算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ...................................................................figure; plot(P1x,P1y…

JeecgBoot v3.5.5 版本发布,性能大升级版本—开源免费的低代码开发平台

项目介绍 JeecgBoot是一款企业级的低代码平台!前后端分离架构 SpringBoot2.x,SpringCloud,Ant Design&Vue3,Mybatis-plus,Shiro,JWT 支持微服务。强大的代码生成器让前后端代码一键生成! JeecgBoot引领…

【Java毕设项目】基于SpringBoot+Vue科研管理系统的设计与实现

博主主页:一季春秋博主简介:专注Java技术领域和毕业设计项目实战、Java、微信小程序、安卓等技术开发,远程调试部署、代码讲解、文档指导、ppt制作等技术指导。主要内容:毕业设计(Java项目、小程序等)、简历模板、学习资料、面试题…

JAVAEE初阶相关内容第十二弹--多线程(进阶)

目录 一、JUC的常见类 1、Callable接口 1.1callable与runnable 1.2代码实例 (1)不使用Callable实现 (2)使用Callable实现 1.3理解Callable 1.4理解FutureTask 2、ReentrantLock 2.1ReentrantLock的用法 2.2ReentrantLoc…

BaseMapper 中的方法

BaseMapper 中的方法&#xff1a; 插入 int insert(T entity) - 插入一条记录。 删除 int deleteById(Serializable id) - 根据主键ID删除记录。 int deleteById(T entity) - 根据实体对象&#xff08;ID&#xff09;删除记录。 int deleteByMap(Map<String, Object> …

快速用Python进行数据分析技巧详解

概要 一些小提示和小技巧可能是非常有用的&#xff0c;特别是在编程领域。有时候使用一点点黑客技术&#xff0c;既可以节省时间&#xff0c;还可能挽救“生命”。 一个小小的快捷方式或附加组件有时真是天赐之物&#xff0c;并且可以成为真正的生产力助推器。所以&#xff0…

【SpringCloud】微服务技术栈入门1 - 远程服务调用、Eureka以及Ribbon

目录 远程服务调用RestTemplate Eureka简要概念配置 Eureka 环境设置 Eureka ClientEureka 服务发现 Ribbon工作流程配置与使用 Ribbon饥饿加载 远程服务调用 RestTemplate RestTemplate 可以模拟客户端来向另外一个后端执行请求 黑马给出的微服务项目中&#xff0c;有两个 …

漏刻有时数据可视化Echarts组件开发(28):异形柱图、pictorialBar和dataZoom组件的使用

构建容器 var dom document.getElementById(container);var myChart echarts.init(dom, null, {renderer: canvas,useDirtyRect: false});模拟数据 var dataList [{name: 班级一, value: 120, max: 120, min: 20},{name: 班级二, value: 183, max: 200, min: 20},{name: 班级…

Windows如何删除“$WINDOWS.~BT“文件夹,解决权限不足无法删除

$WINDOWS.~BT是干嘛的 $Windows.BT是升级或者安装Windows操作系统中间过程中产生的临时文件夹&#xff0c;一般用于保存下载后的升级文件&#xff0c;或者安装过程中复制文件时产生的。用于保存Windows安装记录, 包括配置资料, 错误报告等, 如果安装失败便可反馈给微软公司&am…

pytorch学习3(pytorch手写数字识别练习)

网络模型 设置三层网络&#xff0c;一般最后一层激活函数不选择relu 任务步骤 手写数字识别任务共有四个步骤&#xff1a; 1、数据加载--Load Data 2、构建网络--Build Model 3、训练--Train 4、测试--Test实战 1、导入各种需要的包 import torch from torch import nn f…

Matlab图像处理-区域特征

凹凸性 设P是图像子集S中的点&#xff0c;若通过的每条直线只与S相交一次&#xff0c;则称S为发自P的星形&#xff0c;也就是站在P点能看到S的所有点。 满足下列条件之一&#xff0c;称此为凸状的&#xff1a; 1.从S中每点看&#xff0c;S都是星形的&#xff1b; 2.对S中任…

软件设计师笔记系列(四)

&#x1f600;前言 随着技术的快速发展&#xff0c;软件已经成为我们日常生活中不可或缺的一部分。从智能手机应用到大型企业系统&#xff0c;软件都在为我们提供便利、增强效率和创造价值。然而&#xff0c;随之而来的是对软件质量的日益增长的关注。软件的质量不仅关乎其功能…

C语言中的虚拟地址

虚拟地址 虚拟地址空间 对于操作系统而言&#xff0c;每个进程所得到的虚拟地址都在一个独立的固定的范围内&#xff0c;不会超过这个范围&#xff0c;我们把这个范围称为虚拟地址空间。所谓的虚拟地址空间本质就是一个地址范围&#xff0c;表示程序的寻址能力。对于32位系统…

Python 在 JMeter 中如何使用?

要在JMeter中使用Python&#xff0c;需要使用JSR223 Sampler元素来执行Python脚本。使用JSR223 Sampler执行Python脚本时&#xff0c;需要确保已在JMeter中配置了Python解释器&#xff0c;并设置了正确的环境路径。 1、确保JMeter已安装Python解释器&#xff0c;并将解释器的路…

时序预测 | MATLAB实现POA-CNN-BiGRU鹈鹕算法优化卷积双向门控循环单元时间序列预测

时序预测 | MATLAB实现POA-CNN-BiGRU鹈鹕算法优化卷积双向门控循环单元时间序列预测 目录 时序预测 | MATLAB实现POA-CNN-BiGRU鹈鹕算法优化卷积双向门控循环单元时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现POA-CNN-BiGRU鹈鹕算法优化卷积双向…