如果名称由except ... as绑定,为什么Python 3会引发NameError?

时间:2018-11-06 12:00:22

标签: python python-3.x exception nameerror

为什么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可以修复NameErrorUnboundLocalError

1 个答案:

答案 0 :(得分:4)

这是intentional change in the except semantics,用于解决在回溯中的帧与帧中的异常之间形成参考循环的问题:

  

为了解决与PEP 344相关的垃圾收集问题,除了Python 3中的语句将生成其他字节码以删除目标外,从而消除了参考周期。 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