Python with用法:自动关闭文件进程


Posted in Python onJuly 10, 2019

实际上,Python 提供了 with 语句来管理资源关闭。比如可以把打开的文件放在 with 语句中,这样 with 语句就会帮我们自动关闭文件。

with 语句的语法格式如下:

with context expression [as target(s)]:
  with 代码块

在上面的语法格式中,context_expression 用于创建可自动关闭的资源。

例如,程序使用 with 语句来读取文件:

import codecs
# 使用with语句打开文件,该语句会负责关闭文件
with codecs.open("readlines_test.py", 'r', 'utf-8', buffering=True) as f:
  for line in f:
    print(line, end='')

程序也可以使用 with 语句来处理通过 fileinput.input 合并的多个文件,例如如下程序:

import fileinput
# 使用with语句打开文件,该语句会负责关闭文件
with fileinput.input(files=('test.txt', 'info.txt')) as f:
  for line in f:
    print(line, end='')

上面两个程序都使用了 with 语句来管理资源,因此它们都不需要显式关闭文件。

那么,with 语句的实现原理是什么?其实很简单,使用 with 语句管理的资源必须是一个实现上下文管理协议(context manage protocol)的类,这个类的对象可被称为上下文管理器。要实现上下文管理协议,必须实现如下两个方法:

  • context_manager.__enter__():进入上下文管理器自动调用的方法。该方法会在 with 代码块执行之前执行。如果 with 语句有 as子句,那么该方法的返回值会被赋值给 as 子句后的变量;该方法可以返回多个值,因此,在 as 子句后面也可以指定多个变量(多个变量必须由“()”括起来组成元组)。
  • context_manager.__exit__(exc_type, exc_value, exc_traceback):退出上下文管理器自动调用的方法。该方法会在 with 代码块执行之后执行。如果 with 代码块成功执行结束,程序自动调用该方法,调用该方法的三个参数都为 None:如果 with 代码块因为异常而中止,程序也自动调用该方法,使用 sys.exc_info 得到的异常信息将作为调用该方法的参数。

通过上面的介绍不难发现,只要一个类实现了 __enter__() 和 __exit__(exc_type, exc_value, exc_traceback) 方法,程序就可以使用 with 语句来管理它;通过 __exit__() 方法的参数,即可判断出 with 代码块执行时是否遇到了异常。

换而言之,上面程序所用的文件对象、FileInput 对象,其实都实现了这两个方法,因此它们都可以接受 with 语句的管理。

下面我们自定义一个实现上下文管理协议的类,并使用 with 语句来管理它:

class FkResource:
  def __init__(self, tag):
    self.tag = tag
    print('构造器,初始化资源: %s' % tag)
  # 定义__enter__方法,with体之前的执行的方法
  def __enter__(self):
    print('[__enter__ %s]: ' % self.tag)
    # 该返回值将作为as子句中变量的值
    return 'fkit' # 可以返回任意类型的值
  # 定义__exit__方法,with体之后的执行的方法
  def __exit__(self, exc_type, exc_value, exc_traceback):
    print('[__exit__ %s]: ' % self.tag)
    # exc_traceback为None,代表没有异常
    if exc_traceback is None:
      print('没有异常时关闭资源')
    else:
      print('遇到异常时关闭资源')
      return False  # 可以省略,默认返回None也被看做是False
with FkResource('孙悟空') as dr:
  print(dr)
  print('[with代码块] 没有异常')
print('------------------------------')
with FkResource('白骨精'):
  print('[with代码块] 异常之前的代码')
  raise Exception
  print('[with代码块] ~~~~~~~~异常之后的代码')

上面程序定义了一个 FkResource 类,该类定义了 __enter__() 和 __exit__() 两个方法,因此该类的对象可以被 with 语句管理:

  • 程序在执行 with 代码块之前,会执行 __enter__() 方法,并将该方法的返回值赋值给 as 子句后的变量。
  • 程序在执行 with 代码块之后,会执行 __exit__() 方法,可以根据该方法的参数来判断 with 代码块是否有异常。

程序两次使用 with 语句管理 FkResource 对象。第一次,with 代码块没有出现异常。第二次,with 代码块出现了异常。大家可以看到,使用 with 语句两次对 FkResource 的管理略有差异(主要是在 __exit()__ 方法中略有差异)。

运行上面的程序,可以看到如下输出结果:

构造器,初始化资源: 孙悟空
[__enter__ 孙悟空]:
fkit
[with代码块] 没有异常
[__exit__ 孙悟空]:
没有异常时关闭资源
------------------------------
构造器,初始化资源: 白骨精
[__enter__ 白骨精]:
[with代码块] 异常之前的代码
[__exit__ 白骨精]:
遇到异常时关闭资源
Traceback (most recent call last):
 File "C:\Users\mengma\Desktop\1.py", line 26, in <module>
  raise Exception
Exception

从上面的输出结果来看,使用 with 语句管理资源,程序总可以在进入 with 代码块之前自动执行 __enter__() 方法,无论 with 代码块是否有异常,这个部分都是一样的,而且 __enter__() 方法的返回值被赋值给了 as 子句后的变量,如上面的 ① 号输出信息所示。

对于 with 代码块有异常和无异常这两种情况,此时主要通过 exit() 方法的参数进行判断,程序可针对 with 代码块是否有异常分别进行处理,如程序中代码所示。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python检测网站链接是否已存在
Apr 07 Python
Python创建对称矩阵的方法示例【基于numpy模块】
Oct 12 Python
Python实现学生成绩管理系统
Apr 05 Python
Python实现Pig Latin小游戏实例代码
Feb 02 Python
Pycharm设置utf-8自动显示方法
Jan 17 Python
局域网内python socket实现windows与linux间的消息传送
Apr 19 Python
Python 通过打码平台实现验证码的实现
May 13 Python
解决python中的幂函数、指数函数问题
Nov 25 Python
基于python3实现倒叙字符串
Feb 18 Python
在Sublime Editor中配置Python环境的详细教程
May 03 Python
python之随机数函数的实现示例
Dec 30 Python
有趣的二维码:使用MyQR和qrcode来制作二维码
May 10 Python
Python中的几种矩阵乘法(小结)
Jul 10 #Python
用Python实现最速下降法求极值的方法
Jul 10 #Python
python networkx 根据图的权重画图实现
Jul 10 #Python
python networkx 包绘制复杂网络关系图的实现
Jul 10 #Python
python卸载后再次安装遇到的问题解决
Jul 10 #Python
Python求离散序列导数的示例
Jul 10 #Python
Python Matplotlib 基于networkx画关系网络图
Jul 10 #Python
You might like
用PHP实现小型站点广告管理
2006/10/09 PHP
PHP读取MySQL数据代码
2008/06/05 PHP
php下检测字符串是否是utf8编码的代码
2008/06/28 PHP
php 生成文字png图片的代码
2011/04/17 PHP
php各种编码集详解和以及在什么情况下进行使用
2011/09/11 PHP
PHP调用Linux的命令行执行文件压缩命令
2013/01/27 PHP
Yii2中添加全局函数的方法分析
2017/05/04 PHP
Cookie跨域问题解决方案代码示例
2020/11/24 PHP
自己动手开发jQuery插件教程
2011/08/25 Javascript
jQuery EasyUI API 中文文档 - ComboBox组合框
2011/10/07 Javascript
原生JS和JQuery动态添加、删除表格行的方法
2015/05/28 Javascript
基于jquery animate操作css样式属性小结
2015/11/27 Javascript
Javascript highcharts 饼图显示数量和百分比实例代码
2016/12/06 Javascript
微信小程序 缓存(本地缓存、异步缓存、同步缓存)详解
2017/01/17 Javascript
Vue.js实战之使用Vuex + axios发送请求详解
2017/04/04 Javascript
Angularjs添加排序查询功能的实例代码
2017/10/24 Javascript
解决v-for中使用v-if或者v-bind:class失效的问题
2018/09/25 Javascript
vue.js实现的全选与全不选功能示例【基于elementui】
2018/12/03 Javascript
jQuery无冲突模式详解
2019/01/17 jQuery
Vue实现验证码功能
2019/12/03 Javascript
html中创建并调用vue组件的几种方法汇总
2020/11/17 Javascript
[51:29]Alliance vs TNC 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/18 DOTA
python获取文件扩展名的方法
2015/07/06 Python
Python简单连接MongoDB数据库的方法
2016/03/15 Python
Python3中详解fabfile的编写
2018/06/24 Python
Python django框架开发发布会签到系统(web开发)
2020/02/12 Python
Django数据结果集序列化并展示实现过程
2020/04/22 Python
Python在centos7.6上安装python3.9的详细教程(默认python版本为2.7.5)
2020/10/15 Python
利用css3如何设置没有上下边的列表间隔线
2017/07/03 HTML / CSS
美国现代家具购物网站:LexMod
2019/01/09 全球购物
美国在线医疗分销商:MedEx Supply
2020/02/04 全球购物
财务方面个人工作的自我评价
2013/12/28 职场文书
招聘与培训专员岗位职责
2014/01/30 职场文书
满月酒邀请函
2015/01/30 职场文书
PHP实现rar解压读取扩展包小结
2021/06/03 PHP
Python实现自动玩连连看的脚本分享
2022/04/04 Python