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的工作机制的细节:
- 先评估expression,评估的返回结果是一个对象,这个对象要具有
__enter__
和__exit__
方法,返回的对象称为"环境管理器" - 然后调用环境管理器的
__enter__
方法。__enter__
方法的返回值赋值给 as 指定的变量,或者直接丢弃(没有使用as) - 然后执行with语句块中的内容
- 如果执行with语句块中的内容时抛出了异常,将调用
__exit__(type,value,traceback)
方法,其中这3个和异常相关的参数来源于sys.exc_info
。如果__exit__
返回值为False,则会自动重新抛异常以便传播异常,否则异常被认为合理处理 - 如果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来定义相关操作即可。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]