详解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 相关文章推荐
使用Python制作获取网站目录的图形化程序
May 04 Python
python 性能提升的几种方法
Jul 15 Python
python中正则的使用指南
Dec 04 Python
Python命名空间的本质和加载顺序
Dec 17 Python
Python生成MD5值的两种方法实例分析
Apr 26 Python
使用Python完成15位18位身份证的互转功能
Nov 06 Python
构建高效的python requests长连接池详解
May 02 Python
jupyter notebook运行命令显示[*](解决办法)
May 18 Python
Python几种常见算法汇总
Jun 02 Python
如何实现一个python函数装饰器(Decorator)
Oct 12 Python
解决Pycharm 运行后没有输出的问题
Feb 05 Python
Python办公自动化之教你如何用Python将任意文件转为PDF格式
Jun 28 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
收音机指标测试方法及仪器
2021/03/01 无线电
PHP安装攻略:常见问题解答(三)
2006/10/09 PHP
将OICQ数据转成MYSQL数据
2006/10/09 PHP
php的一个登录的类 [推荐]
2007/03/16 PHP
PHP实现通过中文字符比率来判断垃圾评论的方法
2014/10/20 PHP
关于PHP内置的字符串处理函数详解
2017/02/04 PHP
JavaScript 组件之旅(二)编码实现和算法
2009/10/28 Javascript
js获取页面description的方法
2015/05/21 Javascript
jQuery unbind()方法实例详解
2016/01/19 Javascript
在Mac OS上安装使用Node.js的项目自动化构建工具Gulp
2016/06/18 Javascript
用jQuery向div中添加Html文本内容的简单实现
2016/07/13 Javascript
JS实现焦点图轮播效果的方法详解
2016/12/19 Javascript
echarts3 使用总结(绘制各种图表,地图)
2017/01/05 Javascript
关于在mongoose中填充外键的方法详解
2017/08/14 Javascript
layer弹出层 iframe层去掉滚动条的实例代码
2018/08/17 Javascript
vue 中基于html5 drag drap的拖放效果案例分析
2018/11/01 Javascript
Vue和React组件之间的传值方式详解
2019/01/31 Javascript
vue router 组件的高级应用实例代码
2019/04/08 Javascript
[44:40]Serenity vs Pain 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
[02:08]什么藏在DOTA2 TI9“小紫本”里?斧王历险记告诉你!
2019/05/17 DOTA
python实现可将字符转换成大写的tcp服务器实例
2015/04/29 Python
详解Django 中是否使用时区的区别
2018/06/14 Python
Python 窗体(tkinter)按钮 位置实例
2019/06/13 Python
Python3 操作 MySQL 插入一条数据并返回主键 id的实例
2020/03/02 Python
python实现简单井字棋小游戏
2020/03/05 Python
Django Admin后台添加数据库视图过程解析
2020/04/01 Python
售后服务科岗位职责范文
2013/11/13 职场文书
医学生个人求职信范文
2014/02/07 职场文书
出纳员岗位责任制
2014/02/11 职场文书
酒店管理毕业生自我鉴定
2014/03/02 职场文书
关于诚信的活动方案
2014/08/18 职场文书
群众路线个人对照检查材料2014
2014/09/26 职场文书
小平您好观后感
2015/06/09 职场文书
Win11安装升级时提示“该电脑必须支持安全启动”
2022/04/19 数码科技
macos系统如何实现微信双开? mac登录两个微信以上微信的技巧
2022/07/23 数码科技
HttpClient实现文件上传功能
2022/08/14 Java/Android