从com.lang.python以及其他的一些论坛上看来,一些有经验的python开发者对于在2.5版本新增加的with似乎有一点迷惑。
如同Python的其他方面一样,一旦你了解了with是用来试图解决哪些问题的时候,你会发现其实它很简单。
看看下面的代码片段:
set things up
try:
do something
finally:
tear things down
在这里,”set things up” 可以是打开一个文件或者获得一些外部资源,而”tear things down”可能是相对应的关闭打开的文件或者释放或移除一些资源。try-finally结构保证了就算”do something”代码没有真正执行完成,”tear thigs down”部分也总是会被执行。
如果你经常如上面那样编写一些代码,那么将“set things up”和”tear things down“这些代码放到库函数里以便重用是比较方便的。你可以如下面那样作:
def controlled_execution(callback):
set things up
try:
callback(thing)
finally:
tear things down
def my_function(thing):
do something
controlled_execution(my_function)
但上面的代码有一点冗长,特别是在你需要修改局部变量的时候。另一种方法是使用一次性生成器(译者:one-shot generator是这样翻译?),用for-in声明来”包装“这段代码:
def controlled_execution(callback):
set things up
try:
yield thing
finally:
tear things down
for thing in controlled_execution():
do something with thing
当你只想一次性执行一些代码的时候,上面的代码使用循环结构实现仍然有一点奇怪。所以在考虑了一些备选方案后,GvR和python的开发团队最后终于想出了后面的方法,使用对象来代替生成器来控制一个外埔代码片段的行为:
class controlled_execution:
def __enter__(self):
set things up
return thing
def __exit__(self, type, value, traceback):
tear thigs down
with controlled_execution() as thing:
some code
现在,当执行with语句,python解释器评估with后面的表达式,调用该表达式的__enter__方法,并且将__enter__的返回值赋值给as后面的变量,并且不管发生什么,python一定会执行表达式里面定义的__exit__方法。
作为一个额外的好处,__exit__可以在有异常的时候抑制异常或者做一些必要的异常处理。如果是抑制异常的话只要返回true值就可以。比如下面定义的__exit__方法就仅仅判断了TypeError类型的异常,并放弃处理其他所有的异常:
def __exit__(self, type, value, traceback):
return isinstance(value, TypeError)
在python2.5里面,文件对象已经被预置了__enter__和__exit__方法了:前者简单的返回了文件对象本身而后者则关闭文件:
>>> f = open("x.txt")
>>> f
<open file 'x.txt', mode 'r' at 0x00AE82F0>
>>> f.__enter__()
<open file 'x.txt', mode 'r' at 0x00AE82F0>
>>> f.read(1)
'X'
>>> f.__exit__(None, None, None)
>>> f.read(1)
Traceback (most recent call last):
File "", line 1, in
ValueError: I/O operation on closed file
所以如果要打开一个文件,处理里面的内容并保证关闭打开的文件,你可以简单的这样作:
with open('x.txt') as f:
data = f.read()
do something with data
with声明语法看起来不是那么困难,不是吗?
[[补充]]
- 如果with后面没带as的话,__enter__返回值将被忽略
- __exit__函数的返回值用来指示with-block部分发生的异常是否要re-raise,如果返回False,则会re-raise with-block的异常,如果返回True,则就像什么都没发生
ps:本人不小心看到这篇文章,并很强烈的想做个备份,鉴于是英文的,就自作主张的想翻译一下,可能有很多地方翻译不周到,请指出,谢谢
英文地址:http://effbot.org/zone/python-with-statement.htm