将 Python 上下文管理器的迭代器嵌套在“with”中
我有一个返回上下文管理器的迭代器。
我想要一个 pythonic with
语句,它模拟多个嵌套 with
语句的行为,每个语句对应迭代器返回的每个上下文管理器。
有人可能会说,我想要(已弃用的)contextlib.nested 函数的泛化。
I have an iterator that returns context managers.
I want a pythonic with
statement, that emulates the behaviour of several nested with
statements, one for each context manager returned by the iterator.
One could say, I want a generalisation of the (deprecated) contextlib.nested
function.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
来自 文档:
处理多个上下文管理器的困难在于它们的交互非常重要:例如,您可能
__enter__
第一个,然后在__enter__
第二个中引发异常。这些边缘情况正是导致嵌套被弃用的原因。如果您想支持他们,您必须非常仔细地考虑如何编写代码。您可能希望阅读 PEP-0343 以获取想法。From the docs:
The difficult thing about handling multiple context managers is that they interact non-trivially: for example, you might
__enter__
the first then raise an exception in__enter__
ing the second. These sort of edge cases are precisely what causednested
to be deprecated. If you want to support them, you will have to think very carefully about how you write your code. You may wish to read PEP-0343 for ideas.contextlib.nested
有两个主要问题导致它被弃用。__init__
或__new__
期间引发异常,并且这些异常将导致整个with语句在不调用<外部管理器的code>__exit__。__exit__
中返回True
捕获异常,则仍应执行该块。但在nested
的实现中,它只是引发一个RuntimeError
而不执行该块。这个问题可能需要完全重写nested
。但只需删除
nested
定义中的一个*
就可以解决第一问题!这改变了行为,使得
nested
不再接受参数列表(这无论如何都没有用,因为with
已经可以处理它),而只接受迭代器。因此,我将新版本称为“iter_nested
”。然后,用户可以定义一个迭代器,在迭代期间实例化上下文管理器。
带有生成器的示例:
原始代码和我更改后的
nested
版本的代码之间的差异如下:contextlib.nested
has two major problems that caused it to be deprecated.__init__
or__new__
, and these exceptions would cause the whole with statement to abort without calling__exit__
of the outer manager.True
in__exit__
, the block should still be executed. But in the implementation ofnested
, it just raises aRuntimeError
without executing the block. This problem probably requires a total rewrite ofnested
.But it is possible to solve the first problem by just removing one
*
in the definition ofnested
!This changes the behaviour such that
nested
doesn't accept argument lists anymore (which isn't useful anyway becausewith
can handle that already) but only an iterator. I therefore call the new version "iter_nested
".The user can then define an iterator that instantiates the context managers during iteration.
An example with a generator:
The difference between the codes of the original and my changed version of
nested
is here:古老的问题,但我偶然发现了它。现在的一种解决方案是使用 contextlib.ExitStack():
它处理其他答案中提到的问题。如果输入一个上下文管理器失败,您已输入的其他上下文管理器将正确退出。
对于异步使用,还有
AsyncExitStack
和enter_async_context()
Ancient question but I stumbled upon it. One solution now is to use contextlib.ExitStack():
It handles the problems mentioned in other answers. If entering one context manager fails, the others you have already entered will be exited properly.
For async usage there is also
AsyncExitStack
withenter_async_context()
此实现 - 或或多或少类似的东西,应该执行后期 contextçlib.nested 过去所做的事情,但如果在进入新上下文时引发异常,则要处理已输入的上下文。
上下文可以作为上下文协议对象或元组传递给它,
其中第一个成员是被调用对象,在托管环境中将使用元组的其余部分作为参数进行调用:
This implementation - or something more or less like this, should make what the late contextçlib.nested used to do, but taking care of the already entered contexts if an exception is raised when entering a new context.
Contexts can be passed to it either as a context-protocol object, or as a tuple,
where the first member is a called object that will be called with the remainder of the tuple as parameters, in a managed environment: