66

There is no array type in python, but to emulate it we can use lists. I want to have 2d array-like structure filled in with zeros. My question is: what is the difference, if any, in this two expressions:

zeros = [[0 for i in xrange(M)] for j in xrange(M)]

and

zeros = [[0]*M]*N

Will zeros be same? which one is better to use by means of speed and readability?

yakxxx
  • 2,841
  • 2
  • 21
  • 22
  • 16
    this form `zeros = [[0]*M]*N` will NOT get you want you want because each row will be an instance, so modifying any column will change that column in all rows! if M and N are 3, `zeros[0][1]=2` will result in `[[0,2,0],[0,2,0],[0,2,0]]` – Ezward Jan 11 '20 at 07:12

4 Answers4

106

You should use numpy.zeros. If that isn't an option, you want the first version. In the second version, if you change one value, it will be changed elsewhere in the list -- e.g.:

>>> a = [[0]*10]*10
>>> a
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
>>> a[0][0] = 1
>>> a
[[1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

This is because (as you read the expression from the inside out), you create a list of 10 zeros. You then create a list of 10 references to that initial list of 10 zeros.


Note that:

zeros = [ [0]*M for _ in range(N) ]  # Use xrange if you're still stuck in the python2.x dark ages :).

will also work and it avoids the nested list comprehension. If numpy isn't on the table, this is the form I would use.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • 1
    Is it really necessary to use numpy simply for that single feature? +1 for first version. – John Oct 31 '12 at 12:32
  • 1
    @johnthexiii -- Possibly not. But, if the OP wants a 2d array of zeros, I would be willing to go out on a limb and say that OP's code could probably benefit from numpy in other places as well. – mgilson Oct 31 '12 at 12:34
  • i won't install numpy for just zeroing the list ;) Thanks for later explanation, it was what i was looking for. – yakxxx Oct 31 '12 at 12:36
  • 5
    @yakxxx -- Note that `[ [0]*M for _ in xrange(N) ]` will also work and avoids a nested list-comp (since 0 is immutable) – mgilson Oct 31 '12 at 12:37
  • 1
    First version kill my day! – Valeriy Nov 26 '17 at 14:23
  • Thank you very much. You rescued my time from being wasted. – Soyuzbek Orozbek Uulu Apr 02 '20 at 20:47
  • 1
    i wouldn't use the above solution since this creates references as mentioned by others as well. i tried using this approach while initializing an output matrix for matrix multiplication result and the results were wrong due to the reference issue – Nitin Jul 05 '21 at 05:18
50

for Python 3 (no more xrange), the preferred answer

zeros = [ [0] * N for _ in range(M)]

for M x N array of zeros

Zhe Hu
  • 3,777
  • 4
  • 32
  • 45
  • 1
    I was taking a Python online exam just now, and for whatever reason, the correct answer was `zeros = [[0 for _ in range(N)] for _ in range(M)]`, whereas Zhe Hu's answer didn't give me full marks. Beats me, his version is 4 times faster. – AgentRev Oct 19 '19 at 14:55
  • 2
    His version creates references so if you changed a value, it will change in all rows as explained in the comments above. – Anmol Jagetia Jun 08 '20 at 06:43
  • 2
    @AnmolJagetia This list comprehension won't create `M` references to the same `[0]*N` list; it will create `M` such lists, and you shouldn't see mutations of one row affecting any others. In a list comprehension, e.g. `[expr for _ in range(M)]`, `expr` is evaluated `M` times, which in this case results in `M` lists being created. Meanwhile, `[expr]*M` will evaluate `expr` once, thereby creating `M` references to whatever `expr` evaluates to (`[0]*N` is a non-issue since 0 is an immutable primitive that will be copied). – ClarkZinzow Aug 23 '20 at 00:26
25

In second case you create a list of references to the same list. If you have code like:

[lst] * N

where the lst is a reference to a list, you will have the following list:

[lst, lst, lst, lst, ..., lst]

But because the result list contains references to the same object, if you change a value in one row it will be changed in all other rows.

Ivan Mushketyk
  • 8,107
  • 7
  • 50
  • 67
5

Zhe Hu's answer is the safer one and should have been the best answer. This is because if we use the accepted answer method

a = [[0] * 2] * 2
a[0][0] = 1
print(a)

will give the answer

[[1,0],[1,0]]

So even though you just want to update the first row first column value, all the values in the same column get updated. However

a = [[0] * 2 for _ in range(2)]
a[0][0] = 1
print(a)

gives the correct answer

[[1,0],[0,0]]
Crabigator360
  • 822
  • 10
  • 9