3

I tested the following python code on Spyder IDE. Thinking it would output 2d array q as increasing number as 0..31 from q[0][0] to q[3][7]. But it actually returns q as:

[[24, 25, 26, 27, 28, 29, 30, 31], [24, 25, 26, 27, 28, 29, 30, 31], [24, 25, 26, 27, 28, 29, 30, 31], [24, 25, 26, 27, 28, 29, 30, 31]]. 

The code:

q=[[0]*8]*4 
for i in range(4): 
    for j in range(8): 
        q[i][j] = 8*i+j 
print q

Any idea of what's happening here? I debugged step by step. It shows the updates of every row will sync with all other rows, quite different from my experience of other programing languages.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343

3 Answers3

4
q=[somelist]*4 

creates a list with four identical items, the list somelist. So, for example, q[0] and q[1] reference the same object.

Thus, in the nested for loop q[i] is referencing the same list regardless of the value of i.

To fix:

q = [[0]*8 for _ in range(4)]

The list comprehension evaluates [0]*8 4 distinct times, resulting in 4 distinct lists.


Here is a quick demonstration of this pitfall:

In [14]: q=[[0]*8]*4

You might think you are updating only the first element in the second row:

In [15]: q[1][0] = 100

But you really end up altering the first element in every row:

In [16]: q
Out[16]: 
[[100, 0, 0, 0, 0, 0, 0, 0],
 [100, 0, 0, 0, 0, 0, 0, 0],
 [100, 0, 0, 0, 0, 0, 0, 0],
 [100, 0, 0, 0, 0, 0, 0, 0]]
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • How about j in this case? Is it supposed to be all q[i][0] and q[i][1] reference the same object as well? – user1640608 Sep 02 '12 at 13:07
  • 1
    @user1640608 when you are using primitives (integer, etc), the reference isn't repeated. – Serdalis Sep 02 '12 at 13:09
  • Lists are mutable, integers are not. So if `q[0]` and `q[1]` point to the same list, then `q[0][j] = 100` will modify `q[1][j]` as well. Although `[0]*8` does create a list with 8 references to the same integer, it does not cause a problem because we aren't mutating the integers (an impossibility); we only alter the list's reference to point at other values. – unutbu Sep 02 '12 at 13:18
  • @user1640608: see this old answer of mine: [Python list doesn't reflect variable change, new to python](http://stackoverflow.com/questions/12080552/12080644#12080644); perhaps it'll help you understand what is going on. – Martijn Pieters Sep 02 '12 at 15:50
1

As explained the problem is caused due to * operation on lists, which create more references to the same object. What you should do is to use append:

q=[]
for i in range(4): 
    q.append([])
    for j in range(8): 
        q[i].append(8*i+j)
print q 

[[0, 1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14, 15], [16, 17, 18, 19, 20, 21, 22, 23], [24, 25, 26, 27, 28, 29, 30, 31]]

zenpoy
  • 19,490
  • 9
  • 60
  • 87
0

When you do something like l = [x]*8 you are actually creating 8 references to the same list, not 8 copies.

To actually get 8 copies, you have to use l = [[x] for i in xrange(8)]

>>> x=[1,2,3]
>>> l=[x]*8
>>> l
[[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
>>> l[0][0]=10
>>> l
[[10, 2, 3], [10, 2, 3], [10, 2, 3], [10, 2, 3], [10, 2, 3], [10, 2, 3], [10, 2, 3], [10, 2, 3]]
>>> l = [ [x] for i in xrange(8)]
>>> l
[[[10, 2, 3]], [[10, 2, 3]], [[10, 2, 3]], [[10, 2, 3]], [[10, 2, 3]], [[10, 2, 3]], [[10, 2, 3]], [[10, 2, 3]]]
>>> l[0][0] = 1
>>> l
[[1], [[10, 2, 3]], [[10, 2, 3]], [[10, 2, 3]], [[10, 2, 3]], [[10, 2, 3]], [[10, 2, 3]], [[10, 2, 3]]]
MAK
  • 26,140
  • 11
  • 55
  • 86
  • 2
    Not correct because what you are saying is `[0]*8` will create repeated references, which it will not, only something like `[[0]]*8` will do such a thing because the outer `[]` will contain a reference and not a primitive. – Serdalis Sep 02 '12 at 13:12