将Python上下文管理器的迭代器嵌套在“with”中 [英] Nest an iterator of Python context managers in "with"

查看:92
本文介绍了将Python上下文管理器的迭代器嵌套在“with”中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个返回上下文管理器的迭代器。

I have an iterator that returns context managers.

我想要一个带有语句的pythonic 语句,它模拟了几个嵌套的带有语句的行为,迭代器返回的每个上下文管理器都有一个。

I want a pythonic with statement, that emulates the behaviour of several nested with statements, one for each context manager returned by the iterator.

可以说,我想要推广(已弃用) contextlib.nested 函数。

One could say, I want a generalisation of the (deprecated) contextlib.nested function.

推荐答案

contextlib.nested 有两个主要问题导致它被弃用。

contextlib.nested has two major problems that caused it to be deprecated.


  1. 第一个问题是内部上下文管理器可能会在 __ init __ __ new __ 期间引发异常,这些异常会导致整个with语句在不调用外部管理器的 __ exit __ 的情况下中止。

  2. 第二个问题更复杂。如果其中一个内部经理引发异常而其中一个外部经理通过在 __ exit __ 中返回 True 来捕获它,块仍应执行。但是在嵌套的实现中,它只是在不执行块的情况下引发 RuntimeError 。此问题可能需要完全重写嵌套

  1. first problem is that inner context managers might raise Exceptions during __init__ or __new__, and these exceptions would cause the whole with statement to abort without calling __exit__ of the outer manager.
  2. The second problem is more complicated. If one of the inner managers raises an exception and one of the outer managers catches it by returning True in __exit__, the block should still be executed. But in the implementation of nested, it just raises a RuntimeError without executing the block. This problem probably requires a total rewrite of nested.

但这是可能的通过在嵌套的定义中删除一个 * 来解决第一个问题!
这改变了行为,使得嵌套不再接受参数列表(这无论如何都没用,因为带有已经可以处理了)但只有一个迭代器。因此,我将新版本称为 iter_nested
然后,用户可以定义迭代器,在迭代期间实例化上下文管理器。

But it is possible to solve the first problem by just removing one * in the definition of nested! This changes the behaviour such that nested doesn't accept argument lists anymore (which isn't useful anyway because with 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.

生成器的示例:

def contexts():
    yield MyContext1()
    yield MyContext2()

with iter_nested(contexts()) as contexts:
    do_stuff(contexts[0])
    do_other_stuff(contexts[1])

原始代码与我更改的嵌套版本之间的差异在于:

The difference between the codes of the original and my changed version of nested is here:

from contextlib import contextmanager

@contextmanager
--- def nested(*managers):
+++ def iter_nested(mgr_iterator):
    --- #comments & deprecation warning
    exits = []
    vars = []
    --- exc = (None, None, None)
    +++ exc = None # Python 3
    try:
        --- for mgr in managers:
        +++ for mgr in mgr_iterator:
            exit = mgr.__exit__
            enter = mgr.__enter__
            vars.append(enter())
            exits.append(exit)
        yield vars
# All of the following is new and fit for Python 3
except Exception as exception:
    exc = exception
    exc_tuple = (type(exc), exc, exc.__traceback__)
else:
    exc_tuple = (None, None, None)
finally:
    while exits:
        exit = exits.pop()
        try:
            if exit(*exc_tuple):
                exc = None
                exc_tuple = (None, None, None)
        except Exception as exception:
            exception.__context__ = exc
            exc = exception
            exc_tuple = (type(exc), exc, exc.__traceback__)
    if exc:
        raise exc

这篇关于将Python上下文管理器的迭代器嵌套在“with”中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆