如果 name 被 except...as 绑定,为什么 Python 3 会引发 NameError?

Why does Python 3 raise NameError if name is bound by except...as?

为什么Python 3 在这里提出一个NameError?名称 error 在第一行定义,并在 try...except 块中赋值。这是解释器中的错误,还是我错过了语言定义中从 Python 2 到 3 的细微变化?

error = None

try:
    raise Exception('Boom!')
except Exception as error:
    pass

if error is not None:
    raise error

这是用Python 3.6.7:

执行时的回溯
$ python3 nameerror.py
Traceback (most recent call last):
  File "nameerror.py", line 8, in <module>
    if error is not None:
NameError: name 'error' is not defined

使用 Python 2.7.15,我们得到预期的 Boom!:

$ python2 nameerror.py
Traceback (most recent call last):
  File "nameerror.py", line 9, in <module>
    raise error
Exception: Boom!

如果代码包含在一个函数中,Python 3.6.7 会引发 UnboundLocalError,而 Python 2.7.15 仍会按预期工作。

$ python3 unbound.py
Traceback (most recent call last):
  File "unbound.py", line 13, in <module>
    main()
  File "unbound.py", line 9, in main
    if error is not None:
UnboundLocalError: local variable 'error' referenced before assignment

奇怪的是,从异常处理程序中删除 as error 修复了 NameError resp。 UnboundLocalError.

这是 intentional change in the except semantics 解决的问题,其中在回溯中的帧和帧中的异常之间形成了引用循环:

In order to resolve the garbage collection issue related to PEP 344, except statements in Python 3 will generate additional bytecode to delete the target, thus eliminating the reference cycle. The source-to-source translation, as suggested by Phillip J. Eby [9], is

try:
    try_body
except E as N:
    except_body
...

gets translated to (in Python 2.5 terms)

try:
    try_body
except E, N:
    try:
        except_body
    finally:
        N = None
        del N
...

您可以简单地通过将其分配给其他名称来保留原始异常,例如:

try:
    raise Exception('Boom!')
except Exception as error:
    saved_error = error  # Use saved_error outside the block