为什么Python在使用相同变量名执行嵌套异常时抛出NameError?

jgovgodb  于 2023-02-17  发布在  Python
关注(0)|答案(2)|浏览(126)

我遇到了一个嵌套try/except语句的情况,我不理解Python解释器的行为。
下面是一个与我的用例(连接错误)等效的最小、可重复的示例:

try:
    1 / 0
except ZeroDivisionError as error:
    try:
        [1, 2, 3][3]
    except IndexError as error:
        pass
    print("The last error was:", error)

此示例具有以下输出

Traceback (most recent call last):
  File "/home/sorenmulli/lala.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/sorenmulli/lala.py", line 8, in <module>
    print("The last error was:", error)
NameError: name 'error' is not defined

我希望它输出

The last error was: list index out of range.

我可以通过将代码更改为

try:
    1 / 0
except ZeroDivisionError as error_tmp:
    error = error_tmp
    try:
        [1, 2, 3][3]
    except IndexError as error_tmp:
        error = error_tmp
    print("The last error was:", error)

但在我看来这很奇怪为什么有必要这样做?第二次赋值给error是否应该覆盖第一次赋值:为什么它会完全删除变量?

  • 我使用的是Python 3.9.16,但是我认为输出在Python 3版本之间是一致的。
uelo1irk

uelo1irk1#

此处记录了该行为。
当使用as目标分配异常时,它将在except子句的末尾被清除。

except E as N:
    foo

被翻译成

except E as N:
    try:
        foo
    finally:
        del N

这意味着必须将异常分配给不同的名称,以便能够在except子句之后引用它。异常被清除是因为它们附加了跟踪,它们与堆栈帧形成了一个引用循环,使该帧中的所有局部变量保持活动状态,直到下一次垃圾回收发生。
因此,在您的代码中,error将在except IndexError as error块的末尾被清除。

try:
    1 / 0
except ZeroDivisionError as error:
    try:
        [1, 2, 3][3]
    except IndexError as error:
        pass # `error` will be cleared here
gzszwxb4

gzszwxb42#

第二个错误赋值是否应该覆盖第一个错误赋值:为什么它会完全删除变量?
是的,error变量在内部try-except块中被覆盖,之后一旦我们离开内部块,它就会被删除。
这里Python只是遵循了简单的规则,对try-except块作用域之外的变量进行解引用,但是它也影响了外部块,因为变量是相同的。
试着运行这段代码,你会看到error变量有两个不同的ID。一个ID在第一个try-except块中,另一个ID在内部try-except块中。

try:
    1 / 0
except ZeroDivisionError as error:
    print("ID 1: ", id(error))
    try:
        [1, 2, 3][3]
    except IndexError as error:
        print("ID 2: ", id(error))
        pass
    print("The last error was:", error)

另一件事是你的print语句落在内部try-except块之外,所以在Python解释器到达print("The last error was:", error)行之前,内部try-except块已经完成,因此error变量和NameError异常被删除。
在您描述的第二段代码中,它工作正常,因为您复制了对error变量中对象的引用,所以当删除error_tmp时,您仍然使用error变量引用异常对象。

相关问题