[Tutor] Matrix bug
Steven D'Aprano
steve at pearwood.info
Mon Apr 6 02:38:16 CEST 2015
On Sun, Apr 05, 2015 at 11:12:32AM -0300, Narci Edson Venturini wrote:
> The next code has an unexpected result:
>
> >>>a=3*[3*[0]]
> >>>a
> [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
> >>>a[0][0]=1
> >>>a
> [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
It isn't obvious, and it is *very* common for people to run into this
and be confused, but that is actually working by design. The * operator
for lists does not copy the list items, it makes multiple references to
it. Let's have a look at an example. We start with an arbitrary object:
py> class X: pass
...
py> x = X()
py> print(x)
<__main__.X object at 0xb7a9d92c>
Printing the object x shows us the memory location of x: 0xb7a9d92c. Now
let us put it in a list, and replicate it:
py> mylist = [x]*3
py> print(mylist)
[<__main__.X object at 0xb7a9d92c>, <__main__.X object at 0xb7a9d92c>,
<__main__.X object at 0xb7a9d92c>]
Do you see how *all three* list items have the same memory location?
Rather than get three different X objects, we have the same object,
repeated three times. So these two lines are essentially identical:
mylist = [x]*3
mylist = [x, x, x]
Now, in practice, sometimes that makes a difference, and sometimes it
doesn't. If you use an int or a str, it makes no difference:
py> mylist = [1]*5
py> mylist
[1, 1, 1, 1, 1]
Let's look at their ID numbers and see that they are identical:
py> for item in mylist:
... print(id(item))
...
136560640
136560640
136560640
136560640
136560640
So it is the same int object repeated five times, not five different
objects. But that doesn't matter, since there is no way to change the
value of the object: ints are immutable, and 1 is always 1. You can only
*replace* the object with a new object:
py> mylist[0] = 2
py> print(mylist)
[2, 1, 1, 1, 1]
Now let's do it again with a list of lists:
py> mylist = [[]]*5
py> mylist
[[], [], [], [], []]
py> for item in mylist:
... print(id(item))
...
3081330988
3081330988
3081330988
3081330988
3081330988
So you can see, we now have the same list repeated five times, not five
different lists. If we *replace* one of the items, using = assignment,
everything behaves as expected:
py> mylist[0] = [1,2,3] # Replace the first item.
py> print(mylist)
[[1, 2, 3], [], [], [], []]
But if we modify one of the items, you may be surprised:
py> mylist[1].append(999) # Change the second item in place.
py> print(mylist)
[[1, 2, 3], [999], [999], [999], [999]]
The solution to this "gotcha" is to avoid list multiplication except for
immutable objects like ints and strings. So to get a 3 x 3 array of all
zeroes, I would write:
py> array = [[0]*3 for i in range(3)]
py> print(array)
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
py> array[0][1] += 1
py> print(array)
[[0, 1, 0], [0, 0, 0], [0, 0, 0]]
--
Steve
More information about the Tutor
mailing list