详解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的Tornado框架中的日志的方法
May 02 Python
python中sys.argv参数用法实例分析
May 20 Python
python简单实现刷新智联简历
Mar 30 Python
python中json格式数据输出的简单实现方法
Oct 31 Python
Django Admin中增加导出CSV功能过程解析
Sep 04 Python
使用matplotlib绘制图例标签中带有公式的图
Dec 13 Python
tensorflow入门:TFRecordDataset变长数据的batch读取详解
Jan 20 Python
Python不支持 i ++ 语法的原因解析
Jul 22 Python
Python可以用来做什么
Nov 23 Python
Alpine安装Python3依赖出现的问题及解决方法
Dec 25 Python
python如何获取网络数据
Apr 11 Python
python通配符之glob模块的使用详解
Apr 24 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 checkbox 取值详细说明
2010/08/19 PHP
Mac系统下安装PHP Xdebug
2018/03/30 PHP
JQuery动态给table添加、删除行 改进版
2011/01/19 Javascript
jQuery1.6 正式版发布并提供下载
2011/05/05 Javascript
dojo学习第二天 ajax异步请求之绑定列表
2011/08/29 Javascript
给artDialog 5.02 增加ajax get功能详细介绍
2012/11/13 Javascript
利用cookie记住背景颜色示例代码
2013/11/04 Javascript
5个书写JavaScript代码的坏习惯,看看你中枪了没?
2014/11/06 Javascript
css如何让浮动元素水平居中
2015/08/07 Javascript
jQuery解析Json实例详解
2015/11/24 Javascript
js操作DOM--添加、删除节点的简单实例
2016/07/08 Javascript
微信小程序 开发工具快捷键整理
2016/10/31 Javascript
详解如何提高 webpack 构建 Vue 项目的速度
2017/07/03 Javascript
zTree异步加载展开第一级节点的实现方法
2017/09/05 Javascript
微信小程序报错:this.setData is not a function的解决办法
2017/09/27 Javascript
基于node打包可执行文件工具_Pkg使用心得分享
2018/01/24 Javascript
vue项目中仿element-ui弹框效果的实例代码
2019/04/22 Javascript
3分钟了解vue数据劫持的原理实现
2019/05/01 Javascript
微信小程序开发实现消息推送
2020/11/18 Javascript
layui使用button按钮 点击出现弹层 弹层中加载表单的实例
2019/09/04 Javascript
Vue实现商品飞入购物车效果(电商项目)
2019/11/26 Javascript
用Python设计一个经典小游戏
2017/05/15 Python
python+selenium开发环境搭建图文教程
2017/08/11 Python
深入理解Python3 内置函数大全
2017/11/23 Python
Python使用functools实现注解同步方法
2018/02/06 Python
利用Django提供的ModelForm增删改数据的方法
2019/01/06 Python
Django模型修改及数据迁移实现解析
2019/08/01 Python
Python3合并两个有序数组代码实例
2020/08/11 Python
Python tkinter制作单机五子棋游戏
2020/09/14 Python
HTML5+css3:3D旋转木马效果相册
2017/01/03 HTML / CSS
英国领先的葡萄酒专家:Majestic Wine
2017/05/30 全球购物
您在慕尼黑的跑步商店:Lauf-bar
2019/10/11 全球购物
Booking.com缤客中国:全球酒店在线预订网站
2020/05/03 全球购物
投标人廉洁自律承诺书
2014/05/26 职场文书
课内比教学心得体会
2014/09/09 职场文书
MyBatis配置文件解析与MyBatis实例演示
2022/04/07 Java/Android