3

I've following code which raise Exception:

def a():
     b = []
     def inner():
         b += 3
     inner()
     print (b)

>>> a()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "<console>", line 6, in a
  File "<console>", line 5, in inner
UnboundLocalError: local variable 'b' referenced before assignment

But, If I rewrite the code like this, it runs as expected:

def a():
     b = []
     def inner():
         b.append(5)
     inner()
     print(b)

>>> a()
[5]

I'd like to know why this is happening, thanks.

Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
Shafiul
  • 2,832
  • 9
  • 37
  • 55
  • Note that `+=` on a list is equivalent to `extend`, rather than `append`. Even if you used the `nonlocal` keyword to fix the scoping issue, you'd need to change your first version to `b += [3]` for it to work. – Blckknght Apr 17 '14 at 23:26
  • Yes, I knew it as @Andrew mentioned this in his answer. :) – Shafiul Apr 18 '14 at 05:00

2 Answers2

4

The difference between the two is that b += 3 is an assignment -- it is assigning a totally new value (the result of addition of your empty list and 3) to b. b.append(), in contrast, mutates the list referenced by b without reassigning it.

inner() is accessing a variable in its parent's scope (I think this is not technically a closure since the execution of the parent has not completed). But inner() can only dereference that name, not assign to it, because b is not local to inner() and is not declared as global or nonlocal.

So you can dereference b and mutate the list it refers to, but you cannot assign to it. When you try to assign to it by starting the line with b += you are saying "treat b like a local". On a normal b = 3 assignment this would actually complete successfully, creating a local variable like any other. But in this case, since b has no already assigned value in the local context, dereferencing b in order to perform the addition procedure fails.

As it happens, you can't simply add an integer to a list in the first place. += is not the same thing as append even setting aside assignment vs. mutation, so even if you weren't reaching out of scope it would fail with a TypeError. b += [3] is closer to what you mean, although it will still fail due to the variable's scope.

Andrew Gorcester
  • 19,595
  • 7
  • 57
  • 73
2

Here you can find an exact explanation. http://eli.thegreenplace.net/2011/05/15/understanding-unboundlocalerror-in-python/

If you’re closely following the Python tag on StackOverflow, you’ll notice that the same question comes up at least once a week.

<..>

Although this exact question is answered in Python’s official FAQ (right here), I decided to write this article with the intent of giving a deeper explanation.

<..>

So where does the exception come from? Quoting the FAQ:

This is because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope.

Bruno Gelb
  • 5,322
  • 8
  • 35
  • 50