如何__enter__ n上下文管理器?

wim*_*wim 4 python with-statement contextmanager

使用with语句,我们可以只使用一个级别的缩进/嵌套来输入许多上下文处理程序:

>>> from contextlib import contextmanager
>>> @contextmanager
... def frobnicate(n):
...     print('frobbing {}'.format(n))
...     yield
... 
>>> frob1 = frobnicate(1)
>>> frob2 = frobnicate(2)
>>> with frob1, frob2:
...     pass
... 
frobbing 1
frobbing 2
Run Code Online (Sandbox Code Playgroud)

但这似乎不起作用:

>>> frobs = [frobnicate(1), frobnicate(2)]
>>> with *frobs:
...     pass
# SyntaxError: invalid syntax
Run Code Online (Sandbox Code Playgroud)

我们如何输入n个上下文管理器而不必手动写出每个上下文管理器?

mgi*_*son 6

python2.7使用contextlib.nested来做到这一点,但由于容易出错的怪癖而被弃用.

这个函数有两个主要的怪癖,导致它被弃用.首先,作为上下文管理器都被构造在调用函数前,__new__()__init__()内上下文管理器的方法并不实际上由外上下文管理器的范围所覆盖.这意味着,例如,使用nested()打开两个文件是一个编程错误,因为如果在打开第二个文件时抛出异常,第一个文件将不会立即关闭.

其次,如果__enter__()其中一个内部上下文管理器的方法引发了一个由__exit__()外部上下文管理器的方法捕获和抑制的异常,则此构造将引发RuntimeError而不是跳过with语句的主体.

python3.3确实有一个更好的工作contextlib.ExitStack这将是这样的:

from contextlib import ExitStack

with ExitStack() as stack:
    contexts = [stack.enter_context(frobnicate(i)) for i in range(2)]
    ...
Run Code Online (Sandbox Code Playgroud)

有关backport到python2.x代码,请参阅contextlib2.