代码之家  ›  专栏  ›  技术社区  ›  Mugen

Python在方法调用中使用多个语句变量包装[duplicate]

  •  0
  • Mugen  · 技术社区  · 8 年前

    我使用Python2.7,我知道我可以写以下内容:

    with A() as a, B() as b:
        do_something()
    

    我想提供一个既方便又方便的助手。此助手的用法应如下所示:

    with AB() as ab:
        do_something()
    

    现在AB()应该同时做两件事:创建上下文A()和创建上下文B()。

    我不知道该怎么写这个便民助手

    0 回复  |  直到 9 年前
        1
  •  52
  •   Martijn Pieters    9 年前

    不要重新发明轮子;这并不像看上去那么简单。

    上下文管理器被视为 堆栈 ,并应以与输入相反的顺序退出,例如。如果发生异常,则此订单 事项 ,因为任何上下文管理器都可以抑制该异常,此时,其余的管理器甚至不会收到通知。这个 __exit__ 方法也被允许提出 不同的 然后,其他上下文管理器应该能够处理该新异常。接下来,成功创建 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 然后负责上下文管理器的正确嵌套,正确地、有序地处理退出,并正确地传递异常(包括在被抑制时不传递异常,以及传递新引发的异常)。

    如果你感觉到这两条线( 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:
        # ...
    

    对于Python2,安装 contextlib2 package ,并使用以下导入:

    try:
        from contextlib import ExitStack, contextmanager
    except ImportError:
        # Python 2
        from contextlib2 import ExitStack, contextmanager
    

    这可以避免在Python2上重新发明这个轮子。

    无论你做什么,都要去做 使用 contextlib.nested() ; 这是从Python 3的库中删除的,理由很充分;它也没有实现正确处理嵌套上下文的进入和退出。