2

This is a follow up question to UnboundLocalError on local variable when reassigned after first use.

case-1, the following code

a = 0
def test_immutable():
    a += 1
test_immutable()

encounters an error:

UnboundLocalError: local variable 'a' referenced before assignment

Answers to the original post explains the first case well. a += 1 makes an assignment, and so makes a a local variable that has not been assigned with any object yet, and therefore referencing it causes the UnboundLocalError.

When I replace a with array[0] in the second example below, it works without UnboundLocalError.

case-2, the following code

array = [0, 0, 0]
def test_mutable():
    array[0] += 1
test_mutable()
print(array)

outputs

[1, 0, 0]

I guess it has something to do with a being immutable while array being mutable. But how exactly does Python treat the two cases differently? I'm confused.

martineau
  • 119,623
  • 25
  • 170
  • 301
M Kastar
  • 31
  • 3
  • 5
    It's not an issue of mutability, it's the fact that you are not rebinding the name `array` to something else. `array` points to the same object before, during, and after your function. If you tried: `array += [1]` you would get the same error. – Mark Jul 13 '19 at 22:47
  • 1
    Just as a general note, the rules of variable binding like this are very confusing until you get a good feel for them, but I promise it starts to come naturally once you've played with it enough. – Silvio Mayolo Jul 13 '19 at 22:59

2 Answers2

2

The issue you are observing does not really have anything to do with mutable or immutable types, it is a scoping issue.

Consider, the following:

a = 0
a += 1

This works although 0 is immutable.

Also, this:

array = [0, 0, 0]
def test_mutable():
    array += [1]

test_mutable()
print(array)

does throw you the same UnboundLocalError.

The issue is with scoping. When you try to re-use a inside a function, the interpreter sorts of gets confused at which a you are meaning, because it first realizes that whatever you want to compute, you want to have it with the name a, so it reserves the local name a for you, overriding the global a, but then when you try to use it for the += 1 bit, it realizes that there is nothing bound to the local a.

With the array, and specifically with array[0] the situation is different as you are not reserving the name array for local use, but just keep on using the global array.

norok2
  • 25,683
  • 4
  • 73
  • 99
1

When a global variable is updated in a function like in your case, it should be explicitly be declared as global and this would work fine.

a = 0
def test_immutable():
    global a
    a += 1
test_immutable()

So the case when you were getting an error, it was assuming a as a local variable but there was no declaration before updating it, i.e., a+=1. If you are not updating a global variable in your function, you can use the global variable without explicitly declaring it as global. For example:

a = 0
def test_immutable():
    print(a)
test_immutable()

In the case of a list, you are not updating the whole list, rather updating an element of it. Therefore, it should work without explicitly declaring it as global.

If you try this:

a = [1, 2, 3]
def test_immutable():
    a = [1, 4, 3]
test_immutable()
print(a)

The output would be [1, 2, 3] since the array is getting updated in a local reference. However, if you try this:

a = [1, 2, 3]
def test_immutable():
    global a
    a = [1, 4, 3]
test_immutable()
print(a)

The output would be [1, 4, 3] because values are getting updated in global reference and without any error.

Achint Sharma
  • 355
  • 4
  • 11