4

2I'm relatively new to Python (using 3.3.3) and have a list related question. When modifying a global list variable inside a function (please, no lectures on the evils of global variables), you typically do not need to declare the list with the global keyword inside the function - provided you stick to list methods. In particular, you may not use augmented addition without first using the global keyword. What surprises me is that using augmented addition outside the function clearly isn't modifying the list variable (only the list contents), and so I would expect to be allowed to use it inside a function without using the global keyword. Here are two examples that I can't reconcile:

list_1 = []

def my_func():
    list_1.append(0)
    #list_1 += [0]

my_func()
print('list_1 =', list_1)

This prints list_1 = [0], as expected, while the commented out augmented addition operation generates a complaint about using a local variable before assignment.

Here's an example that I can't reconcile with the previous one:

list_1 = [0]
list_2 = list_1
list_1 += [1]
print('list_2 =', list_2)

This prints list_2 = [0, 1], which suggests to me that list_1 += [1] did not modify the list_1 variable. I'm aware that list_1 = list[1] + [1] qualifies as modifying list_1, but augmented addition doesn't seem to. Why does augmented addition, inside a function, require use of the global keyword? Thanks for any help understanding this.

Jon
  • 63
  • 7
  • 1
    Did you try printing out `list_1` to check your hypothesis that `list_1 += [1]` didn't modify `list_1`? – juanchopanza May 02 '14 at 20:29
  • You say "This prints `list_2 = [0, 1]`, which suggests to me that `list_1 += [1]` did not modify the `list_1` variable." Why does that suggest that to you? – BrenBarn May 02 '14 at 20:34
  • @BrenBarn I believe Jon means that `list_1 += [1]` did not cause `list_1` to point to a different list, whereas `list_1 = list_1 + [1]` creates a new list and has `list_1` point to that new list. – Rob Watts May 02 '14 at 20:38
  • @ Rob Watts Yes, that's exactly what I mean. – Jon May 02 '14 at 22:51
  • possible duplicate of [UnboundLocalError in Python](http://stackoverflow.com/questions/9264763/unboundlocalerror-in-python) – Martijn Pieters May 09 '14 at 12:48

1 Answers1

5

The problem is when a function body is parsed all the variables used in either normal assignments or augmented assigments are considered as local variables, so when the function gets called Python will not look for those variables in global scope hence it will raise an error. Hence you need to specify those variables as global to tell Python to look for them in global scope.

Another alternative is to use list.extend() instead of +=.

Related:

Community
  • 1
  • 1
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • I would say it is a matter of readability and consistency. if `list_1` was an int, `+=` would reassign, and therefore it would make sense that `global` must be used. It makes sense that `+=` will have the same effect on globalness for all types – njzk2 May 02 '14 at 20:44
  • FYI, OP asked about Python 3.x, but your first link is the Python 2.x docs. – Rob Watts May 02 '14 at 20:45
  • @njzk2 If I got your point correctly then for immutable types it doesn't matter whether you use `+=` or plain `=`, because in any way it going to update only a single variable and all the other references are going to be unaffected. So, `+=` is going to have a different effect based on the object's type. – Ashwini Chaudhary May 02 '14 at 20:54
  • @RobWatts Updated the link, except the `nonlocal` part they're essentially the same. – Ashwini Chaudhary May 02 '14 at 20:54
  • @Aशwiniचhaudhary: yes. If you look at `a = 3; id(a); a += 1; id(a)` vs `lst = []; id(lst); lst += [0]; id(lst)`, you'll see that `+=` reassigns the immutable type (has to, obviously) but does not reassign the list. – njzk2 May 02 '14 at 21:01
  • (all of which because `__iadd__` falls back on `__add__` for classes where `__iadd__` is not defined) – njzk2 May 02 '14 at 21:03
  • @Aशwini चhaudhary That sounds reasonable. So, identifying the object on which a method is called is handled differently? Meaning, when `list_1.append(0)` is parsed in `my_func()`, `list_1` is **not** considered to be a local variable? What is the reason for this difference? (The extend() method looks helpful!) – Jon May 02 '14 at 23:06
  • @Jon Yes, Other things like fetching the value of an object follow the LEGB rule. One reason could be optimization, value lookup for locals is very fast. – Ashwini Chaudhary May 03 '14 at 04:04
  • @Jon I'd suggest you to read [Understanding UnboundLocalError in Python](http://eli.thegreenplace.net/2011/05/15/understanding-unboundlocalerror-in-python/) and [Python internals: Symbol tables, part 1](http://eli.thegreenplace.net/2010/09/18/python-internals-symbol-tables-part-1/). – Ashwini Chaudhary May 03 '14 at 04:08
  • @Aशwini चhaudhary When I read your UnboundLocalError link above, I wasn't able to recognize an explanation, but I _was_ able to understand the UnboundLocalError link you just posted. My simple take-away is that since `list_1.append(0)` involves only name resolution, rather than assignment or binding, `list_1` is resolved by the nearest enclosing scope. A interesting distinction, and one which also explains why I can use local variables in functions, provided that I don't try to modify them. Thank you! – Jon May 03 '14 at 15:25
  • I just noticed a typo in my last response - "...why I can use local variables..." should be "...why I can use global variables...". – Jon May 18 '14 at 16:11