"except Foo as bar" 导致 "bar" 从范围中删除

"except Foo as bar" causes "bar" to be removed from scope

给定以下代码:

msg = "test"
try:
    "a"[1]
except IndexError as msg:
    print("Error happened")
print(msg)

有人可以解释为什么这会导致 Python 3 中出现以下输出吗?

Error happened
Traceback (most recent call last):
  File "test.py", line 6, in <module>
    print(msg)
NameError: name 'msg' is not defined

是的,一旦引发异常并为 msg 分配新的异常对象,原始对象将不再有引用,因此将被删除。新的异常对象也会在离开 except 块后立即被删除。

你可以通过覆盖对象的__del__方法和分配给msg的异常来验证它:

class A:
    def __del__(self):
        print('object deleted')
class E(Exception):
    def __del__(self):
        print('exception deleted')
msg = A()
try:
    raise E()
except E as msg:
    print("Error happened")

这输出:

object deleted
Error happened
exception deleted
NameError: name 'msg' is not defined
except 子句中的

msg 与第一行的 msg 在同一范围内。

但是in Python 3 we have this new behavior too:

When an exception has been assigned using as target, it is cleared at the end of the except clause. This is as if

except E as N:
    foo

was translated to

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

This means the exception must be assigned to a different name to be able to refer to it after the except clause. Exceptions are cleared because with the traceback attached to them, they form a reference cycle with the stack frame, keeping all locals in that frame alive until the next garbage collection occurs.

因此,您 "overwrite msg" 在异常处理程序中,退出处理程序将删除变量以清除回溯引用循环。

异常块在块的末尾删除捕获的变量,但它们没有自己的作用域。所以事件的顺序是:

1) msg 设置为局部范围内的某个字符串

2) msg 被设置为与 1

在同一本地范围内的 IndexError 对象

3) msg 在 Exception 块结束时从局部作用域中删除

4) msg 不再在本地范围内定义,因此访问它的尝试失败