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
_______________________________________________
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

Reply via email to