2.1.3 上下文管理器

上下文管理器是带有enterexit方法的对象,在with语句中使用:

In [ ]:

  1. with manager as var:
  2. do_something(var)

最简单的等价case是

In [ ]:

  1. var = manager.__enter__()
  2. try:
  3. do_something(var)
  4. finally:
  5. manager.__exit__()

换句话说,在PEP343定义的上下文管理器协议,使将try..except..finally结构中枯燥的部分抽象成一个独立的类,而只保留有趣的do_something代码块成为可能。

  • 首先调用enter方法。它会返回一个值被赋值给varas部分是可选的:如果不存在,enter返回的值将被忽略。
  • with下面的代码段将被执行。就像try从句一样,它要么成功执行到最后,要么breakcontinue或者return,或者它抛出一个异常。无论哪种方式,在这段代码结束后,都将调用exit。如果抛出异常,关于异常的信息会传递给exit,将在下一个部分描述。在一般的情况下,异常将被忽略,就像finally从句一样,并且将在exit结束时重新抛出。 假如我们想要确认一下文件是否在我们写入后马上关闭:

In [23]:

  1. class closing(object):
  2. def __init__(self, obj):
  3. self.obj = obj
  4. def __enter__(self):
  5. return self.obj
  6. def __exit__(self, *args):
  7. self.obj.close()
  8. with closing(open('/tmp/file', 'w')) as f:
  9. f.write('the contents\n')

这里我们确保当with代码段退出后,f.close()被调用。因为关闭文件是非常常见的操作,对这个的支持已经可以在file类中出现。它有一个exit方法,调用了close并且被自己用于上下文管理器:

In [ ]:

  1. with open('/tmp/file', 'a') as f:
  2. f.write('more contents\n')

try..finally的常用用途是释放资源。不同的情况都是类似的实现:在enter阶段,是需要资源的,在exit阶段,资源被释放,并且异常,如果抛出的话,将被传递。就像with文件一样,当一个对象被使用后通常有一些自然的操作,最方便的方式是由一个内建的支持。在每一次发布中,Python都在更多的地方提供了支持: