将两个上下文管理器合二为一
Combine two context managers into one
我使用 Python 2.7,我知道我可以这样写:
with A() as a, B() as b:
do_something()
我想提供一个兼顾两者的便利助手。这个助手的用法应该是这样的:
with AB() as ab:
do_something()
现在 AB() 应该同时执行:创建上下文 A() 和创建上下文 B()。
我不知道如何编写这个方便的助手
不要重新发明轮子;这并不像看起来那么简单。
上下文管理器被视为一个 堆栈 ,并且应该以与它们进入的相反顺序退出,例如。如果发生异常,这个顺序 很重要 ,因为任何上下文管理器都可以抑制异常,此时其余的管理器甚至不会收到通知。 __exit__
方法也被允许引发 different 异常,然后其他上下文管理器应该能够处理该新异常。接下来,成功创建 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
负责上下文管理器的正确嵌套,按顺序正确处理退出,并正确传递异常(包括在抑制时不传递异常,并传递新的异常) -ly 引发异常)。
如果觉得两行(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:
# ...
对于 Python 2,安装 contextlib2
package,并使用以下导入:
try:
from contextlib import ExitStack, contextmanager
except ImportError:
# Python 2
from contextlib2 import ExitStack, contextmanager
这可以让您避免在 Python 2 上重新发明这个轮子。
无论你做什么,不要使用contextlib.nested()
;由于非常充分的理由,它已在 Python 3 中从库中删除;它也没有正确实现处理嵌套上下文的进入和退出。
我使用 Python 2.7,我知道我可以这样写:
with A() as a, B() as b:
do_something()
我想提供一个兼顾两者的便利助手。这个助手的用法应该是这样的:
with AB() as ab:
do_something()
现在 AB() 应该同时执行:创建上下文 A() 和创建上下文 B()。
我不知道如何编写这个方便的助手
不要重新发明轮子;这并不像看起来那么简单。
上下文管理器被视为一个 堆栈 ,并且应该以与它们进入的相反顺序退出,例如。如果发生异常,这个顺序 很重要 ,因为任何上下文管理器都可以抑制异常,此时其余的管理器甚至不会收到通知。 __exit__
方法也被允许引发 different 异常,然后其他上下文管理器应该能够处理该新异常。接下来,成功创建 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
负责上下文管理器的正确嵌套,按顺序正确处理退出,并正确传递异常(包括在抑制时不传递异常,并传递新的异常) -ly 引发异常)。
如果觉得两行(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:
# ...
对于 Python 2,安装 contextlib2
package,并使用以下导入:
try:
from contextlib import ExitStack, contextmanager
except ImportError:
# Python 2
from contextlib2 import ExitStack, contextmanager
这可以让您避免在 Python 2 上重新发明这个轮子。
无论你做什么,不要使用contextlib.nested()
;由于非常充分的理由,它已在 Python 3 中从库中删除;它也没有正确实现处理嵌套上下文的进入和退出。