28.7. contextlib —具有语句状态的 Util

2.5 版的新Function。

源代码: Lib/contextlib.py


该模块为涉及with语句的常见任务提供 Util。有关更多信息,另请参见上下文 Management 器类型使用语句上下文 Management 器

Functions provided:

  • contextlib. contextmanager(* func *)
    • 此函数是decorator,可用于为with语句上下文 Management 器定义工厂函数,而无需创建类或单独的enter()exit()方法。

尽管许多对象本机支持在 with 语句中使用,但有时需要 Management 的资源本身不是上下文 Management 器,并且没有实现与contextlib.closing一起使用的close()方法

下面是一个抽象的示例,以确保正确的资源 Management:

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子句中的目标(如果有)。

在生成器屈服的 Moment,执行嵌套在with语句中的块。退出该块后,将恢复生成器。如果在块中发生未处理的异常,则会在生成器内部产生yield的位置将其重新引发。因此,您可以使用tryexceptfinally语句来捕获错误(如果有),或确保进行一些清除。如果仅出于记录日志或执行某些操作(而不是完全抑制它)的目的捕获了异常,则生成器必须重新引发该异常。否则,生成器上下文 Management 器将向with语句指示已处理了该异常,并且将在with语句之后立即使用该语句 continue 执行。

  • contextlib. nested(* mgr1 * [,* mgr2 * [,* ... *]])
    • 将多个上下文 Management 器组合到一个嵌套的上下文 Management 器中。

不赞成使用此Function,而推荐使用with语句的多 Management 器形式。

with语句的多 Management 器形式相比,此Function的一个优势是参数解压缩使它可以与可变数量的上下文 Management 器一起使用,如下所示:

from contextlib import nested

with nested(*managers):
    do_something()

请注意,如果嵌套上下文 Management 器之一的exit()方法指示应抑制异常,则不会将异常信息传递给任何剩余的外部上下文 Management 器。同样,如果其中一个嵌套 Management 器的exit()方法引发异常,则任何先前的异常状态都将丢失;新的异常将被传递给所有其余外部上下文 Management 器的exit()方法。通常,exit()方法应避免引发异常,尤其是它们不应重新引发传入的异常。

此Function有两个主要的怪癖,导致不推荐使用。首先,由于上下文 Management 器都是在调用函数之前构建的,因此内部上下文 Management 器的new()init()方法实际上并未包含在外部上下文 Management 器的范围内。例如,这意味着使用nested()打开两个文件是一个编程错误,因为如果在打开第二个文件时引发异常,则第一个文件将不会立即关闭。

其次,如果内部上下文 Management 器之一的enter()方法引发被外部上下文 Management 器之一的exit()方法捕获并抑制的异常,则此构造将引发RuntimeError而不是跳过with语句的主体。

需要支持可变数量的上下文 Management 器嵌套的开发人员可以使用warnings模块来抑制此Function引发的 DeprecationWarning,也可以将该Function用作特定于应用程序实现的模型。

从 2.7 版开始不推荐使用:with 语句现在直接支持此Function(不存在容易出错的易错问题)。

  • contextlib. closing(* thing *)
    • 返回一个上下文 Management 器,该 Management 器在块完成时关闭* 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()

See also

  • PEP 343-“ with”语句

  • Python with语句的规范,背景和示例。