Reference semantics provides some insight into the different approaches needed to use and to change a list.
Frirst consider the case where you just want to use the values in a list. Perhaps they represent the lengths of bars to draw, e.g.
magnitudes = [41, 14, 27, 5, 32]
for m in magnitudes:
print m*'='
This works nicely producing the output,
>>> magnitudes = [41, 14, 27, 5, 32]
>>> for m in magnitudes:
print m*'='
=========================================
==============
===========================
=====
================================
>>>
Now suppose we want to add 10% to each value in the list. We might reason
that in the for loop above m takes the value of each entry in the list, so
changing it will change the values in the list and so try to do what we want
like this,
for m in magnitudes:
m = m + m/10
but testing will reveal that it doesn't do what we want,
>>> magnitudes = [41, 14, 27, 5, 32]
>>> for m in magnitudes:
m = m + m/10
>>> print magnitudes
[41, 14, 27, 5, 32]
>>>
because the list is unchanged. Have you already spotted the problem? The first part of our thinking that "m takes on the value of each entry in the list" was correct. The problem is with our second part "changing it will change the values in the list". Here's what memory looks like at the start of the first iteration of the loop and after the assignment statement inside has executed,
for m in magnitudes: |
|
m = m + m/10 |
|
Notice carefully what happened. The expression m + m/10 was evaluated 41 +
41/10 → 41 + 4 → 45 yielding the object 45, so the name m was set
to refer to this object (remember: "Assignment creates a reference to an
object"!) leaving the list unchanged.
There are a number of ways to get around this problem. The most general* is to access the list contents by index,
for i in range( len(magnitudes )):
magnitudes[i] = magnitudes[i] + magnitudes[i]/10
and testing shows it works,
>>> for i in range( len(magnitudes )):
magnitudes[i] = magnitudes[i] + magnitudes[i]/10
>>> print magnitudes
[45, 15, 29, 5, 35]
>>>
But why does it work? Because the assignment statement on the first
iteration becomes magnitudes[0] = magnitudes[0] +
magnitudes[0]/10. And magnitudes[0] is the precise name
of the reference that is the first entry in the list so we are changing it
directly. Similarly on the later iterations we will change
magnitudes[1], magnitudes[2] and so on. Remember
that the entries in magnitudes do have indirect names!
Another approach is to create a new list with the modified values and then assign it to the old name, e.g.
tmp = []
for m in magnitudes:
tmp.append(m + m/10)
magnitudes = tmp
For more Pythonic approaches to the problem consult Pythonic Details.
When working with lists be clear in your own mind whether you are using the elements in the list, or modifying them and choose your coding approach accordingly.
For use,
for item in thelist:
# do stuff with item
For modification,
for i in range( len(thelist) ):
thelist[i] = ...
* Most general because it can be done across many languages. Some would also say it is the least "Pythonic", but it's still the first one for us to consider because the aim of this course is to teach programming not just Python.