详解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 lxml模块安装教程
Jun 02 Python
Python实现优先级队列结构的方法详解
Jun 02 Python
用Python将动态GIF图片倒放播放的方法
Nov 02 Python
python队列通信:rabbitMQ的使用(实例讲解)
Dec 22 Python
python psutil库安装教程
Mar 19 Python
Python操作word常见方法示例【win32com与docx模块】
Jul 17 Python
pycharm执行python时,填写参数的方法
Oct 29 Python
解决python 3 urllib 没有 urlencode 属性的问题
Aug 22 Python
python+selenium select下拉选择框定位处理方法
Aug 24 Python
python实现文字版扫雷
Apr 24 Python
PyCharm2019.3永久激活破解详细图文教程,亲测可用(不定期更新)
Oct 29 Python
python 用opencv实现图像修复和图像金字塔
Nov 27 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
Terran兵种对照表
2020/03/14 星际争霸
使用PHP socke 向指定页面提交数据
2008/07/23 PHP
解析PHP跳出循环的方法以及continue、break、exit的区别介绍
2013/07/01 PHP
PHP使用DirectoryIterator显示下拉文件列表的方法
2015/03/13 PHP
以文件形式缓存php变量的方法
2015/06/26 PHP
Laravel 实现Eloquent模型分组查询并返回每个分组的数量 groupBy()
2019/10/23 PHP
jquery与js函数冲突的两种解决方法
2013/09/09 Javascript
AngularJS中的模块详解
2015/01/29 Javascript
js轮播图代码分享
2016/07/14 Javascript
jQuery中show与hide方法用法示例
2016/09/16 Javascript
微信小程序 扎金花简单实例
2017/02/21 Javascript
详解angularjs中的隔离作用域理解以及绑定策略
2017/05/31 Javascript
原生JS获取元素的位置与尺寸实现方法
2017/10/18 Javascript
轻量级JS Cookie插件js-cookie的使用方法
2018/03/22 Javascript
Angular6 写一个简单的Select组件示例
2018/08/20 Javascript
node.js express捕获全局异常的三种方法实例分析
2019/12/27 Javascript
原生小程序封装跑马灯效果
2020/10/21 Javascript
[01:10]DOTA2次级职业联赛 - Fly战队宣传片
2014/12/01 DOTA
Python中random模块用法实例分析
2015/05/19 Python
Python中将字典转换为列表的方法
2016/09/21 Python
Django框架创建mysql连接与使用示例
2019/07/29 Python
对于Python深浅拷贝的理解
2019/07/29 Python
Python 70行代码实现简单算式计算器解析
2019/08/30 Python
TensorFlow——Checkpoint为模型添加检查点的实例
2020/01/21 Python
详解Html5中video标签那些属性和方法
2019/07/01 HTML / CSS
Html5页面二次分享的实现
2018/07/30 HTML / CSS
百度JavaScript笔试题
2015/01/15 面试题
营销与策划个人求职信
2013/09/22 职场文书
关于母亲节的感言
2014/02/04 职场文书
小学清明节活动方案
2014/03/08 职场文书
蟋蟀的住宅教学反思
2014/04/26 职场文书
就业协议书
2014/09/12 职场文书
拾金不昧表扬稿
2015/01/16 职场文书
2014年度个人工作总结范文
2015/03/09 职场文书
MySQL中你可能忽略的COLLATION实例详解
2021/05/12 MySQL
php 文件上传至OSS及删除远程阿里云OSS文件
2021/07/04 PHP