with 语句的等效 try 语句是什么?

What is the equivalent try statement of the with statement?

在阅读了 Python 的语言文档的 with statement section 之后,我想知道这样说 Python 代码是否正确:

with EXPRESSION as TARGET:
    SUITE

相当于这个:

try:
    manager = (EXPRESSION)
    value = manager.__enter__()
    TARGET = value  # only if `as TARGET` is present in the with statement
    SUITE
except:
    import sys
    if not manager.__exit__(*sys.exc_info()):
        raise
else:
    manager.__exit__(None, None, None)

编辑

正确的等效 Python 代码(真正的 CPython 实现在 C 中)实际上是由 Guido van Rossum 自己在 PEP 343:

中给出的
mgr = (EXPR)
exit = type(mgr).__exit__  # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
    try:
        VAR = value  # Only if "as VAR" is present
        BLOCK
    except:
        # The exceptional case is handled here
        exc = False
        if not exit(mgr, *sys.exc_info()):
            raise
        # The exception is swallowed if exit() returns true
finally:
    # The normal and non-local-goto cases are handled here
    if exc:
        exit(mgr, None, None, None)

从 Python 3.6 开始,这发生了一点变化:现在 __enter__ 函数在 __exit__ 函数之前被加载 (参见https://bugs.python.org/issue27100).

所以我的等效 Python 代码有三个缺陷:

  1. 在调用 __enter__ 函数之前应加载 __enter____exit__ 函数。
  2. __enter__函数应该在try语句之外调用(参见语言文档中第4点的注释)。
  3. else 子句应该改为 finally 子句,以处理存在非本地 goto 语句(breakcontinuereturn) 在 suite.

但是我不明白为什么 PEP 343 中的等效 Python 代码将 finally 子句放在外部 try 语句而不是内部try声明?

Nick Coghlan,PEP 343, answered on the Python bug tracker 的另一位作者:

It's a matter of historical timing: PEP 343 was written before try/except/finally was allowed, when try/finally and try/except were still distinct statements.

However, PEP 341 was also accepted and implemented for Python 2.5, allowing for the modern try/except/finally form: https://docs.python.org/dev/whatsnew/2.5.html#pep-341-unified-try-except-finally

因此 with 语句的现代 try 语句等效 Python 代码是:

manager = (EXPRESSION)
enter = type(manager).__enter__
exit = type(manager).__exit__
value = enter(manager)
hit_except = False

try:
    TARGET = value  # only if `as TARGET` is present in the with statement
    SUITE
except:
    import sys
    hit_except = True
    if not exit(manager, *sys.exc_info()):
        raise
finally:
    if not hit_except:
        exit(manager, None, None, None)