[关闭]
@Frankchen 2016-03-24T11:38:43.000000Z 字数 1785 阅读 1612

理解Python中的with指令

python


with指令相对于Python里其他的内容来说是很普通的,前提是你理解了它所要解决的问题。考虑一下以下的这些代码:

  1. set things up
  2. try:
  3. do something
  4. finally:
  5. tear things down

在这里,“set things up” 可能是打开一个文件或者取得一些其他的资源,而“tear things down”可能是关闭这个文件或者发布或者移除这个资源。而try-finally结构保证了“tear things down”部分总是被执行,甚至是在执行这个功能的代码没有结束的情况下也是如此。
若是你经常使用这个功能,把 “set things up”和“tear things down” 写进库函数来复用是很方便的。你可以如下操作:

  1. def controlled_execution(callback):
  2. set things up
  3. try:
  4. callback(thing)
  5. finally:
  6. tear things down
  7. def my_function(thing):
  8. do something
  9. controlled_execution(my_function)

然而这样有点太繁琐,特别是你需要改变局部变量的时候。另一种方法则是使用一次性的产生器,并且使用for-in指令来“包裹”代码:

  1. def controlled_execution():
  2. set things up
  3. try:
  4. yield thing
  5. finally:
  6. tear things down
  7. for thing in controlled_execution():
  8. do something with thing

但是,在Python2.4版本以前yieldtry-finally内部是不被允许的。而当这个问题被修复以后(已在2.5版本被修复),而当你知道你只需要执行某些功能一次的时候去使用循环,这仍然是比较怪异的。
所以在考虑了许多替代者之后,GvR和python-dev team之后终于得出了一般化的结论,即使用一个对象(object)而不是产生器来控制一些额外的代码的行为:

  1. class controlled_execution:
  2. def __enter__(self):
  3. set things up
  4. return thing
  5. def __exit__(self, type, value, traceback):
  6. tear things down
  7. with controlled_execution() as thing:
  8. some code

现在,当with被执行后,Python评估这个表达,在结果值上引用__enter__方法(被叫做上下文看守),并且分配所有__enter__返回的值给予as给的变量。Python将执行代码的本体,在这代码中并且无论发生什么,召唤守卫对象的__exit__方法。
另外,__exit__方法能够寻找例外情况,必要时禁止它或者执行它。
若要禁止例外,只需要返回一个真值即可。比如,接下来的__exit__方法禁止所有的TypeError,但是允许所有的其他例外通过:

  1. def __exit__(self, type, value, traceback):
  2. # isinstance判断前者是否为后者,返回Ture或者False
  3. return isinstance(value, TypeError)

在Python2.5中,文件对象本身自带有__enter____exit__方法,前者返回文件对象本身,而后者关闭文件:

  1. >>> f = open("x.txt")
  2. >>> f
  3. <open file 'x.txt', mode 'r' at 0x00AE82F0>
  4. >>> f.__enter__()
  5. <open file 'x.txt', mode 'r' at 0x00AE82F0>
  6. >>> f.read(1)
  7. 'X'
  8. >>> f.__exit__(None, None, None)
  9. >>> f.read(1)
  10. Traceback (most recent call last):
  11. File "<stdin>", line 1, in <module>
  12. ValueError: I/O operation on closed file

因此,要打开文件,处理其内容,并且需要确保关闭此文件的时候,简单来说你可以这样做:

  1. with open("x.txt") as f:
  2. data = f.read()
  3. do something with data

这不是很难,对吗?

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注