我使用Python 2.7,我知道我可以编写以下代码:
with A() as a, B() as b:
do_something()
我希望提供一个方便的帮助器,它可以同时执行两个操作。使用这个帮助器应该像这样:
with AB() as ab:
do_something()
现在AB()应该做两件事情:创建上下文A()和创建上下文B()。
我不知道如何编写这个便利帮助程序。
我使用Python 2.7,我知道我可以编写以下代码:
with A() as a, B() as b:
do_something()
我希望提供一个方便的帮助器,它可以同时执行两个操作。使用这个帮助器应该像这样:
with AB() as ab:
do_something()
现在AB()应该做两件事情:创建上下文A()和创建上下文B()。
我不知道如何编写这个便利帮助程序。
不要重复发明轮子;这并不像看起来那么简单。
上下文管理器被视为一个堆栈,并且应该按照它们进入的相反顺序退出,例如。如果发生异常,这个顺序很重要,因为任何上下文管理器都可能抑制异常,此时其余的管理器甚至不会被通知到这一点。__exit__方法还允许引发不同的异常,其他上下文管理器应该能够处理新的异常。接下来,成功创建A()意味着如果B()由于异常而失败,则应该得到通知。
现在,如果你想做的只是创建一定数量的上下文管理器,你可以事先使用@contextlib.contextmanager装饰器在生成器函数上:
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()
实现即可:from contextlib import ExitStack
with ExitStack() as stack:
cms = [stack.enter_context(cls()) for cls in (A, B)]
# ...
< p > ExitStack
然后负责正确嵌套上下文管理器,按顺序正确处理退出,并正确传递异常(包括在抑制时不传递异常,并传递新引发的异常)。
如果您认为这两行(with
和单独调用 enter_context()
)太麻烦了,可以使用一个单独的 @contextmanager
修饰的生成器函数:
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:
# ...
对于Python 2,请安装contextlib2
包,并使用以下导入:
try:
from contextlib import ExitStack, contextmanager
except ImportError:
# Python 2
from contextlib2 import ExitStack, contextmanager
contextlib.nested()
;Python 3中已经将其从库中删除,原因非常充分;它也没有正确实现处理嵌套上下文的进入和退出。
a
或b
或ab
- 这就引出了一个问题:为什么不只用with AB():
呢? - Martin Bonner supports Monica