将上下文管理器的动态迭代链接到单个 with 语句

Ric*_*ann 5 python iterable with-statement chain python-3.x

我有一堆想要链接的上下文管理器。乍一看,contextlib.nested看起来像是一个合适的解决方案。但是,此方法在文档中被标记为已弃用,该文档还指出最新with声明允许直接执行此操作:

2.7 版后已弃用: with 语句现在直接支持此功能(没有容易混淆的错误怪癖)。

但是我无法让 Python 3.4.3 使用上下文管理器的动态迭代:

class Foo():
    def __enter__(self):
        print('entering:', self.name)
        return self
    def __exit__(self, *_):
        pass
    def __init__(self, name):
        self.name = name

foo = Foo('foo')
bar = Foo('bar')
Run Code Online (Sandbox Code Playgroud)

是否链式:

from itertools import chain
m = chain([foo], [bar])
with m:
     pass

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: __exit__
m = [foo, bar]
Run Code Online (Sandbox Code Playgroud)

直接提供列表:

with m:
     pass

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: __exit__
Run Code Online (Sandbox Code Playgroud)

或开箱:

with (*m):
    pass

  File "<stdin>", line 1
SyntaxError: can use starred expression only as assignment target
Run Code Online (Sandbox Code Playgroud)

那么,如何正确地在with语句中正确链接动态数量的上下文管理器?

Mar*_*ers 8

你误解了那条线。该with语句采用多个上下文管理器,用逗号分隔,但不是可迭代的:

with foo, bar:
Run Code Online (Sandbox Code Playgroud)

作品。

如果您需要支持一组动态的上下文管理器,请使用contextlib.ExitStack()对象

from contextlib import ExitStack

with ExitStack() as stack:
    for cm in (foo, bar):
        stack.enter_context(cm)
Run Code Online (Sandbox Code Playgroud)

  • @CecilCurry:它维护了一堆上下文管理器(和回调钩子),当上下文退出时,它们以相反的顺序调用。我认为这个名字很好地涵盖了这一点,实际上!很高兴我的回答有帮助 ;-) (2认同)