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来定义相关操作即可。

contextlib模块

在自定义上下文管理器的时候,可以通过contextlib模块的contextmanager装饰器来简化,这样不需要自己去写__enter____exit__

使用contextlib.contextmanager装饰的时候,所装饰的对象必须是一个生成器函数,且该生成器函数必须只yield一个值,这个值将会被绑定到with/as的as子句的变量上。

from contextlib import contextmanager

@contextmanager
def managed_resource(*args, **kwds):
# Code to acquire resource, e.g.:
resource = acquire_resource(*args, **kwds)
try:
yield resource
finally:
# Code to release resource, e.g.:
release_resource(resource) >>> with managed_resource(timeout=3600) as resource:
... # Resource is released at the end of this block,
... # even if code in the block raises an exception

它的执行流程是这样的:

  1. 当执行到with语句的时候,首先评估managed_resource(timeout=3600),它是一个生成器函数,它会返回一个生成器
  2. 然后执行resource = acquire_resource,并在yield的地方状态被挂起这个生成器函数
  3. 在yield挂起的时候,with语句块中的语句开始执行
  4. 当with语句块退出的时候,yield被恢复,于是继续执行生成器函数中后面的语句

如果在with语句块中发生了异常且未处理,则会在生成器yield被恢复的时候再次抛出这个异常。因此,可以使用try...except...finally语句来捕获可能存在的错误,以便保证yield后面的语句(通常是资源释放类的善后工作)可以正常执行。

Python with/as和contextlib上下文管理使用说明的更多相关文章

  1. python contextlib 上下文管理器

    1.with操作符 在python中读写文件,可能需要这样的代码 try-finally读写文件 file_text = None try: file_text = open('./text', 'r ...

  2. contextlib 上下文管理器

    在Python中,读写文件这样的资源要特别注意,必须在使用完毕后正确关闭它们.正确关闭文件资源的一个方法是使用try...finally: try: f = open('/path/to/file', ...

  3. 流畅的python第十五章上下文管理器和else块学习记录

    with 语句和上下文管理器for.while 和 try 语句的 else 子句 with 语句会设置一个临时的上下文,交给上下文管理器对象控制,并且负责清理上下文.这么做能避免错误并减少样板代码, ...

  4. python实现可以被with上下文管理的类或函数

      # .开始之前先明确一下with机制 # 1.类包函数__enter__()和__exit__()函数,即是可以被上下文管理的类 # __enter__用来执行with时的方法,__exit__返 ...

  5. python框架之Flask(4)-上下文管理

    知识储备 偏函数 作用 偏函数,帮助开发者自动传递参数. 使用 import functools def index(a1, a2): return a1 + a2 # 原来的调用方式 # ret = ...

  6. (转)contextlib — 上下文管理器工具

    原文:https://pythoncaff.com/docs/pymotw/contextlib-context-manager-tool/95 这是一篇社区协同翻译的文章,你可以点击右边区块信息里的 ...

  7. Python全栈day28(上下文管理)

    我们知道在操作文件对象的时候可以这么写 with open('a.txt',''r) as f: 代码 上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明 ...

  8. python中的with与上下文管理器

    #转载请留言联系 很多人平时需要打开文件进行读取写入操作时,通常这样: f = open('文件路径','w') f.write(data) f.close 这样写有一个潜在的问题,如果在调用 wri ...

  9. python深浅拷贝&垃圾回收&上下文管理(with语句)

    深浅拷贝 在Python中使用copy模块用于对象的拷贝操作. 该模块提供了两个主要的方法:浅拷贝 copy.copy() 深拷贝 copy.deepcopy() 1.浅拷贝(copy) 浅拷贝: 不 ...

随机推荐

  1. Linux下好用的屏幕录像软件kazam及截图软件shutter

    都是apt直接安装即可使用. 其中kazam默认保存的文件格式是avi,非常大,通常录制几十秒就已经好几个G,导致录制过程太占用资源,会出现卡顿的现象. 在“首选项”中可以选择输出格式为mp4,文件就 ...

  2. Jquery 在子页面上设置父页面元素的值

    使用情景:因为我父页面上有用art.dialog,而子页面上有项目中的框架弹出方法跟art.dialog冲突,不能使用art.dialog自带的方法传值, 所以只好用一种简单粗暴的方法来设置. var ...

  3. Django积木块七——视频

    视频 # 在网上搜索video.js然后下载相关的js和css文件,看文档正确使用视频模块,添加视频外链 <div style="width: 1200px;height: 675px ...

  4. git安装以及初始化

    安装文档参见:https://www.cnblogs.com/ximiaomiao/p/7140456.html 注意:安装成功后,用cmd进行基本信息设置时,当出现“git不是内部或外部命令,也不是 ...

  5. 一把梭系列 之 颜值不够VsCode来凑

    如果您的孩子不适应编译型语言怎么办? 如果您的孩子贪玩不想花多时间在编程上怎么办? 如果您还没有孩子怎么办? 如果您夜晚兼职觉不够睡又怎么办? 不妨试试 “ 拍 簧 片 ”. 妈了巴子的有点麻烦,但别 ...

  6. 亲子编程玩Micro:bit-动力小车“麦昆”

    少儿编程之风已经吹进各大城市,编程猫.乐博机器人.童程童美等专业培训机构逐渐进入大家的视野,年龄段已经从K12逐渐降低到幼儿园中班.其实,少儿编程的门槛并不高,它不会让孩子一上手就去接触代码,而是会通 ...

  7. Windows 10 IoT Core 17133 for Insider 版本更新

    今天,微软发布了Windows 10 IoT Core 17133 for Insider 版本更新,本次更新只修正了一些Bug,没有发布新的特性.用户可以登录Windows Device Porta ...

  8. CSS中的px与物理像素、逻辑像素、1px边框问题

    一直不太清楚CSS中的1px与逻辑像素.物理像素是个什么关系(作为一名前端感觉很惭愧 -_-!),今天终于花时间彻底弄清楚了,其实弄清楚之后就觉得事情很简单,但也只有在弄清楚之后,才会觉得简单(语出& ...

  9. 理解 Linux 的虚拟内存

    前言 前不久组内又有一次我比较期待的分享:”Linux 的虚拟内存”.是某天晚上加班时,我们讨论虚拟内存的概念时,leader 发现几位同事对虚拟内存认识不清后,特意给这位同学挑选的主题(笑). 我之 ...

  10. <mvc:annotation-driven> 中的HttpMessageConverters 的理解

    用烂的图 配置一个或多个HttpMessageConverter类型以用于转换@RequestBody方法 参数和@ResponseBody方法返回值. 使用此配置元素是可选的.  此处提供的Http ...