首页 > 解决方案 > 如果 name 由 except...as 绑定,为什么 Python 3 会引发 NameError?

问题描述

为什么 Python 3 在NameError这里提出 a ?名称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相应的问题。UnboundLocalError.

标签: pythonpython-3.xexceptionnameerror

解决方案


这是语义上的有意更改,except以解决在回溯中的帧和帧中的异常之间形成引用循环的问题:

为了解决 PEP 344 相关的垃圾回收问题,Python 3 中的 except 语句会生成额外的字节码来删除目标,从而消除引用循环。正如 Phillip J. Eby [9] 所建议的,源到源的翻译是

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

被翻译成(在 Python 2.5 术语中)

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

推荐阅读