with 块中多个 __exit__ 调用的顺序是否始终是确定的?

Is the order of multiple __exit__ calls in a with-block always deterministic?

给定以下 ContextManager 类,这是以下示例中 __exit__ 调用的顺序 - 声明的相反顺序。

有趣的是,__exit__可以被调用多次。这在 2.7 的所有 Python 版本中都是确定性的吗?如果不是,从哪个版本这个顺序是可靠的确定性的?

a = A()
b = B()
c = C()

with a, b, c, a, a:
    print("Start")

测试工具

from typing import ContextManager

class A(ContextManager):
    def __exit__(self, __exc_type, __exc_value, __traceback):
        print("A exit")

class B(ContextManager):
    def __exit__(self, __exc_type, __exc_value, __traceback):
        print("B exit")

class C(ContextManager):
    def __exit__(self, __exc_type, __exc_value, __traceback):
        print("C exit")

a = A()
b = B()
c = C()

with a, b, c, a, a:
    print("Start")

Python 3.7+ 输出:

Start
A exit
A exit
C exit
B exit
A exit

同一行 with 上的多个上下文管理器被视为嵌套处理。所以这个:

with a, b, c:
    stuff()

相当于:

with a:
    with b:
        with c:
            stuff()

__enter__方法从左到右调用,当块结束时,__exit__方法从右到左调用。

Is this deterministic in all versions of Python from 2.7?

是的,顺序明确since 2.7 at least:

with A() as a, B() as b:
   ... suite of statements ...

is equivalent to:

with A() as a:
   with B() as b:
       ... suite of statements ...

你得到的顺序(从下往上读)

Start
A exit
A exit
C exit
B exit
A exit

确实是A、B、C、A、A,果然如所料。