28.7. contextlib — with 语句上下文的实用程序 — Python 文档
28.7. 上下文库 — 实用程序和 -语句上下文
2.5 版中的新功能。
源代码: :source:`Lib/contextlib.py`
该模块为涉及 with 语句的常见任务提供实用程序。 有关更多信息,另请参阅 上下文管理器类型 和 带有语句上下文管理器 。
提供的功能:
- contextlib.contextmanager(func)
该函数是一个装饰器,可用于为和语句上下文管理器定义工厂函数,无需创建类或将
__enter__()
和__exit__()
方法。虽然许多对象本身支持在 with 语句中使用,但有时需要管理的资源本身不是上下文管理器,并且不实现
contextlib.closing
的close()
方法]以下是一个抽象示例,以确保正确的资源管理:
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
被装饰的函数在调用时必须返回一个 generator-iterator。 这个迭代器必须正好产生一个值,该值将绑定到 with 语句的 as 子句中的目标,如果有的话。
在生成器产生时,嵌套在 with 语句中的块被执行。 然后在退出块后恢复生成器。 如果块中发生未处理的异常,它会在生成器内在产生的点处重新引发。 因此,您可以使用 try...except...finally 语句来捕获错误(如果有),或确保进行一些清理。 如果一个异常被捕获只是为了记录它或执行某些操作(而不是完全抑制它),则生成器必须重新引发该异常。 否则,生成器上下文管理器将向 with 语句指示异常已被处理,并且将继续执行紧跟在 with 语句之后的语句。
- contextlib.nested(mgr1[, mgr2[, ...]])
将多个上下文管理器组合成一个嵌套的上下文管理器。
此函数已被弃用,取而代之的是 with 语句的多管理器形式。
与 with 语句的多管理器形式相比,此函数的一个优点是参数解包允许它与可变数量的上下文管理器一起使用,如下所示:
from contextlib import nested with nested(*managers): do_something()
请注意,如果嵌套上下文管理器之一的
__exit__()
方法指示应抑制异常,则不会将异常信息传递给任何剩余的外部上下文管理器。 同样,如果嵌套管理器之一的__exit__()
方法引发异常,则任何先前的异常状态都将丢失; 新的异常将传递给任何剩余的外部上下文管理器的__exit__()
方法。 一般来说,__exit__()
方法应该避免引发异常,特别是它们不应该重新引发传入的异常。这个函数有两个主要的怪癖导致它被弃用。 首先,由于上下文管理器都是在调用函数之前构建的,因此内部上下文管理器的
__new__()
和__init__()
方法实际上并未包含在外部上下文管理器的范围内。 这意味着,例如,使用 nested() 打开两个文件是一个编程错误,因为如果打开第二个文件时抛出异常,第一个文件将不会立即关闭。其次,如果内部上下文管理器之一的
__enter__()
方法引发异常,该异常被外部上下文管理器之一的__exit__()
方法捕获并抑制,则该构造将引发RuntimeError
而不是跳过 with 语句的主体。需要支持嵌套可变数量的上下文管理器的开发人员可以使用 warnings 模块来抑制此函数引发的 DeprecationWarning,或者使用此函数作为应用程序特定实现的模型。
自 2.7 版起已弃用: with 语句现在直接支持此功能(没有容易混淆的错误怪癖)。
- contextlib.closing(thing)
返回一个上下文管理器,在块完成时关闭 thing。 这基本上相当于:
from contextlib import contextmanager @contextmanager def closing(thing): try: yield thing finally: thing.close()
并让您编写这样的代码:
from contextlib import closing import urllib with closing(urllib.urlopen('http://www.python.org')) as page: for line in page: print line
无需明确关闭
page
。 即使发生错误,退出with块时也会调用page.close()
。