global
is only needed when you are trying to change the object which the variable references. Because vals[0] = 5
changes the actual object rather than the reference, no error is raised. However, with vals += [5, 6]
, the interpreter tries to find a local variable because it can't change the global variable.
The confusing thing is that using the +=
operator with list modifies the original list, like vals[0] = 5
. And whereas vals += [5, 6]
fails, vals.extend([5, 6])
works. We can enlist the help of dis.dis
to lend us some clues.
>>> def a(): v[0] = 1
>>> def b(): v += [1]
>>> def c(): v.extend([1])
>>> import dis
>>> dis.dis(a)
1 0 LOAD_CONST 1 (1)
3 LOAD_GLOBAL 0 (v)
6 LOAD_CONST 2 (0)
9 STORE_SUBSCR
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
>>> dis.dis(b)
1 0 LOAD_FAST 0 (v)
3 LOAD_CONST 1 (1)
6 BUILD_LIST 1
9 INPLACE_ADD
10 STORE_FAST 0 (v)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
d
>>> dis.dis(c)
1 0 LOAD_GLOBAL 0 (v)
3 LOAD_ATTR 1 (extend)
6 LOAD_CONST 1 (1)
9 BUILD_LIST 1
12 CALL_FUNCTION 1
15 POP_TOP
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
We can see that functions a
and c
use LOAD_GLOBAL
, whereas b
tries to use LOAD_FAST
. We can see now why using +=
won't work - the interpreter tries to load v
as a local variable because of it's default behaviour with in-place addition. Because it can't know whether v
is a list or not, it essentially assumes that the line means the same as v = v + [1]
.