pythonpython-3.xscoping

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


Given the following code:

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

Can somebody explain why this causes the following output in Python 3?

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

Solution

  • msg in the except clause is in the same scope as msg on the first line.

    But 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.

    so, you "overwrite msg" in the exception handler, and exiting the handler will delete the variable to clear the traceback reference cycle.