详解Python with/as使用说明


Posted in Python onDecember 13, 2018

with/as

使用open打开过文件的对with/as都已经非常熟悉,其实with/as是对try/finally的一种替代方案。

当某个对象支持一种称为"环境管理协议"的协议时,就会通过环境管理器来自动执行某些善后清理工作,就像finally一样:不管中途是否发生异常,最终都会执行某些清理操作。

用法:

with expression [as var]:
 with_block_code

当expression返回的对象是支持环境管理协议的时候,就可以使用with。as var是可选的,如果不使用as var,expression返回对象将被丢弃,如果使用as var,就会将expression的返回对象赋值给变量var。

整个流程大致如下:先评估expression,如果支持环境管理协议,然后开始with/as语句块结构,当准备退出with语句块的时候,将执行对象中定义的善后操作。工作机制的细节见下文。

例如,open()返回的文件对象是支持环境管理协议的,所以可以用with/as来安全地打开文件:

with open(r'd:\a\b\c\a.log') as logfile:
 for line in logfile:
  print(line)
  ...more code here...

整个过程是先open(),然后with/as,输出每一行后将要退出with语句块的时候,环境管理器根据文件对象中定义的操作关闭文件。

它实际上等价于:

myfile = open(r'd:\a\b\c\a.log')
try:
 for line in myfile:
  print(line)
  ...more code here...
finally:
 myfile.close()

虽然在文件不被引用之后,垃圾回收器会自动回收这个文件对象,但是垃圾回收器的回收操作是有等待时间的。换句话说,如果不使用with/as打开文件,也不显示close()关闭文件,那么这个文件很可能会在用完之后保持空闲一段时间,然后才被垃圾回收器回收。

with/as不仅用于文件打开/关闭,锁操作也支持环境管理协议,也就是说,在有需要的时候会自动释放锁资源。

嵌套多个环境管理器

在python 3.1之后,with as支持多个环境管理器,使用逗号隔开即可。

with A() as a, B() as b:
 ...statements...

它等价于嵌套的with:

with A() as a:
 with B() as b:
  ...statements...

多环境管理器管理的多个对象会在with语句块中出现异常的时候,或者执行完with语句块的时候全部自动被清理(例如文件关闭操作)。

例如,打开两个文件,将它们的内容通过zip()合并在一起,并且同时关闭它们:

with open('a.file') as f1, open('b.file') as f2:
 for pair in zi[(f1, f2):
  print(pair)

自定义环境管理器

无论是文件还是锁,都是别人已经写好了环境管理器的对象。我们自己也可以写环境管理器,让它可以使用with/as,这实际上属于运算符重载的范畴。

要写自己的环境管理器,先了解with/as的工作机制的细节:

  1. 先评估expression,评估的返回结果是一个对象,这个对象要具有 __enter__ __exit__ 方法,返回的对象称为"环境管理器"
  2. 然后调用环境管理器的 __enter__ 方法。 __enter__ 方法的返回值赋值给 as 指定的变量,或者直接丢弃(没有使用as)
  3. 然后执行with语句块中的内容
  4. 如果执行with语句块中的内容时抛出了异常,将调用 __exit__(type,value,traceback) 方法,其中这3个和异常相关的参数来源于 sys.exc_info 。如果 __exit__ 返回值为False,则会自动重新抛异常以便传播异常,否则异常被认为合理处理
  5. 如果with语句块中的内容没有抛异常,则直接调用 __exit__(None,None,None) ,即这三个参数都传递为None值

看一个简单的示例:

class TraceBlock:
 def message(self, arg):
  print('running ' + arg)

 def __enter__(self):
  print('starting with block')
  return self

 def __exit__(self, exc_type, exc_value, exc_tb):
  if exc_type is None:
   print('exited normally\n')
  else:
   print('raise an exception! ' + str(exc_type))
   return False

上面的 __enter__ 方法返回的对象会赋值给as关键字指定的变量,在这个示例中即将对象自身返回。如果有需求,可以返回其它对象。

上面的 __exit__ 中,如果异常的类型为None,说明with语句块中的语句执行过程没有抛异常,正常结束即可。但是如果有异常,则要求返回False,实际上上面的 return False 可以去掉,因为函数没有return时默认返回None,它的布尔值代表的就时False。

测试下:

with TraceBlock() as action:
 action.message("test 1")
 print("reached")
 
print('-' * 20, "\n")

with TraceBlock() as action:
 action.message("test 2")
 raise TypeError
 print("not reached")

结果如下:

starting with block
running test 1
reached
exited normally

--------------------

starting with block
running test 2
raise an exception! <class 'TypeError'>
Traceback (most recent call last):
  File "g:/pycode/list.py", line 23, in <module>
    raise TypeError
TypeError

定义环境管理器不是件简单的事。一般来说,如果不是很复杂的需求,直接使用try/finally来定义相关操作即可。

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

Python 相关文章推荐
使用rpclib进行Python网络编程时的注释问题
May 06 Python
Python+django实现简单的文件上传
Aug 17 Python
Python实现针对给定字符串寻找最长非重复子串的方法
Apr 21 Python
django输出html内容的实例
May 27 Python
python中使用print输出中文的方法
Jul 16 Python
使用Py2Exe for Python3创建自己的exe程序示例
Oct 31 Python
Python+pyplot绘制带文本标注的柱状图方法
Jul 08 Python
Python 二叉树的层序建立与三种遍历实现详解
Jul 29 Python
Python socket聊天脚本代码实例
Jan 02 Python
Python 判断时间是否在时间区间内的实例
May 16 Python
python实现测试工具(二)——简单的ui测试工具
Oct 19 Python
python openpyxl模块的使用详解
Feb 25 Python
在python中以相同顺序shuffle两个list的方法
Dec 13 #Python
对python GUI实现完美进度条的示例详解
Dec 13 #Python
PyTorch 1.0 正式版已经发布了
Dec 13 #Python
修改python plot折线图的坐标轴刻度方法
Dec 13 #Python
python画图系列之个性化显示x轴区段文字的实例
Dec 13 #Python
Python实现的各种常见分布算法示例
Dec 13 #Python
Python线性拟合实现函数与用法示例
Dec 13 #Python
You might like
解析php中如何调用用户自定义函数
2013/08/06 PHP
Yii中CGridView关联表搜索排序方法实例详解
2014/12/03 PHP
Laravel 5框架学习之路由、控制器和视图简介
2015/04/07 PHP
PHP入门教程之自定义函数用法详解(创建,调用,变量,参数,返回值等)
2016/09/11 PHP
PHP7如何开启Opcode打造强悍性能详解
2018/05/11 PHP
PDO::inTransaction讲解
2019/01/28 PHP
redis+php实现微博(三)微博列表功能详解
2019/09/23 PHP
WEB页子窗口(showModalDialog和showModelessDialog)使用说明
2009/10/25 Javascript
基于Jquery的$.cookie()实现跨越页面tabs导航实现代码
2011/03/03 Javascript
js 实现菜单上下显示附效果图
2013/11/21 Javascript
常见浏览器多长时间会提示“脚本运行时间过长”总结
2014/04/29 Javascript
jQuery实现简单的间隔向上滚动效果
2015/03/09 Javascript
通用javascript代码判断版本号是否在版本范围之间
2015/11/29 Javascript
JS图片定时翻滚效果实现方法
2016/06/21 Javascript
Bootstrap使用基础教程详解
2016/09/05 Javascript
详解如何在Vue里建立长按指令
2018/08/20 Javascript
详解如何在vue项目中使用layui框架及采坑
2019/05/05 Javascript
vue导入.md文件的步骤(markdown转HTML)
2020/12/31 Vue.js
[02:26]DOTA2英雄米拉娜基础教程
2013/11/25 DOTA
[05:45]Ti4观战指南(下)
2014/07/07 DOTA
python3生成随机数实例
2014/10/20 Python
python实现的守护进程(Daemon)用法实例
2015/06/02 Python
python生成IP段的方法
2015/07/07 Python
Python获取命令实时输出-原样彩色输出并返回输出结果的示例
2019/07/11 Python
如何基于Python批量下载音乐
2019/11/11 Python
django 读取图片到页面实例
2020/03/27 Python
Sentry错误日志监控使用方法解析
2020/11/12 Python
给海归自荐信的建议
2013/12/13 职场文书
《听鱼说话》教学反思
2014/02/15 职场文书
党校培训自我鉴定范文
2014/03/20 职场文书
工作保证书范文
2014/04/29 职场文书
家长通知书家长意见
2014/12/30 职场文书
2015年中职班主任工作总结
2015/05/25 职场文书
vue完美实现el-table列宽自适应
2021/05/08 Vue.js
python自动化测试之Selenium详解
2022/03/13 Python
修改Nginx配置返回指定content-type的方法
2022/09/23 Servers