不要重新发明轮子;这并不像看上去那么简单。
上下文管理器被视为
堆栈
,并应以与输入相反的顺序退出,例如。如果发生异常,则此订单
事项
,因为任何上下文管理器都可以抑制该异常,此时,其余的管理器甚至不会收到通知。这个
__exit__
方法也被允许提出
不同的
然后,其他上下文管理器应该能够处理该新异常。接下来,成功创建
A()
意味着如果
B()
因异常而失败。
现在,如果你只想创建一个
固定的
你预先知道的上下文管理器的数量,只需使用
@contextlib.contextmanager
decorator
在生成器函数上:
from contextlib import contextmanager
@contextmanager
def ab_context():
with A() as a, B() as b:
yield (a, b)
然后将其用作:
with ab_context() as ab:
如果你需要处理
变量
多个上下文管理器,然后不要构建自己的实现;使用标准库
contextlib.ExitStack()
implementation
相反:
from contextlib import ExitStack
with ExitStack() as stack:
cms = [stack.enter_context(cls()) for cls in (A, B)]
# ...
这个
ExitStack
然后负责上下文管理器的正确嵌套,正确地、有序地处理退出,并正确地传递异常(包括在被抑制时不传递异常,以及传递新引发的异常)。
如果你感觉到这两条线(
with
,并分别致电
enter_context()
)太单调了,你可以用一个单独的
@contextmanager
-decorated generator function
:
from contextlib import ExitStack, contextmanager
@contextmanager
def multi_context(*cms):
with ExitStack() as stack:
yield [stack.enter_context(cls()) for cls in cms]
然后使用
ab_context
这样地:
with multi_context(A, B) as ab:
# ...
对于Python2,安装
contextlib2
package
,并使用以下导入:
try:
from contextlib import ExitStack, contextmanager
except ImportError:
# Python 2
from contextlib2 import ExitStack, contextmanager
这可以避免在Python2上重新发明这个轮子。
无论你做什么,都要去做
不
使用
contextlib.nested()
; 这是从Python 3的库中删除的,理由很充分;它也没有实现正确处理嵌套上下文的进入和退出。