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

Question:

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
Asked By: knipknap

||

Answers:

Yes, as soon as the exception is raised and msg is assigned with the new exception object, the original object has no more reference and is therefore deleted. The new exception object is also deleted as soon as it leaves the except block.

You can verify it by overriding the __del__ method of the object and the exception assigned to 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")

This outputs:

object deleted
Error happened
exception deleted
NameError: name 'msg' is not defined
Answered By: blhsing

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.

Answered By: Uku Loskit

Exception blocks delete the caught variable at the end of the block, but they do not have their own scopes. So the sequence of events goes:

1) msg is set to some string in the local scope

2) msg is set to an IndexError object in the same local scope as 1

3) msg is deleted from the local scope when the Exception block ends

4) msg is no longer defined in the local scope, so the attempt to access it fails

Answered By: Michael
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.