3

I came across a weird difference with local variable scopes between Python 2.7 and Python 3.7.

Consider this artificial script unboundlocalexception.py (I realize that I could just have used an else-block after except, but I extracted this example from a longer function):

def foo():
  arithmetic_error = None
  try:
    y = 1.0 / 0
  except ZeroDivisionError as arithmetic_error:
    print("I tried to divide by zero")

  if arithmetic_error is None:
    print("Correct division")

foo()

Under Python 2 it works as I expected it to:

$ python2 unboundlocalexception.py 
I tried to divide by zero

But, surprisingly, under Python 3 an UnboundLocalError is raised!

$ python3 unboundlocalexception.py 
I tried to divide by zero
Traceback (most recent call last):
  File "unboundlocalexception.py", line 11, in <module>
    foo()
  File "unboundlocalexception.py", line 8, in foo
    if arithmetic_error is None:
UnboundLocalError: local variable 'arithmetic_error' referenced before assignment

Is this difference documented anywhere?

rerx
  • 1,133
  • 8
  • 19

1 Answers1

3

This behaviour was introduced in Python 3, to prevent reference cycles, because the exception target - arithmetic_error in the question - keeps a reference to the traceback.

From the Language Reference

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.

This was originally documented in PEP3110.

snakecharmerb
  • 47,570
  • 11
  • 100
  • 153
  • 1
    It really looks like you **copied** this answer: https://stackoverflow.com/a/29268974. Instead of copying you should just flag the question as a duplicate ;) And give credit to the person who really answered.... – Riccardo Bucco Dec 19 '19 at 13:53
  • 1
    I simply looked at the docmentation, and quoted it - I wasn't aware of the other answer when I answered. But since you have found a good duplicate, I will close this question. – snakecharmerb Dec 19 '19 at 13:57
  • @RiccardoBucco Or at least I would have done, had not, not user chepner beaten me to it. – snakecharmerb Dec 19 '19 at 13:58