67

Let's say I have the following empty two dimensional array in Python:

q = [[None]*5]*4

I want to assign a value of 5 to the first row in the first column of q. Instinctively, I do the following:

q[0][0] = 5

However, this produces:

 [[5, None, None, None, None], 
  [5, None, None, None, None], 
  [5, None, None, None, None], 
  [5, None, None, None, None]]

The first element of every array is being initialized to 5, where I thought only the first element of the first array would get the update. I have two questions:

  1. Why is Python initializing the first value of every array and not just the first one?
  2. Is there a better way to accomplish what I'm trying to do?
Ben McCormack
  • 32,086
  • 48
  • 148
  • 223

7 Answers7

110

This doesn't do what you hoped.

q = [[None]*5]*4

It reuses list objects multiple times. As you can see when you made a change to one cell, which was in a reused list object.

A single list with a value of [None] is used five times.

A single list with a value of [[None]*5] is used four times.

q = [ [ None for i in range(5) ] for j in range(4) ]

Might be more what you're looking for.

This explicitly avoids reusing a list object.

80% of the time, a dictionary is what you really wanted.

q = {}
q[0,0]= 5

Will also work. You don't start with a pre-defined grid of None values. But it's rare to need them in the first place.

In Python 2.7 and higher, you can do this.

q = { (i,j):0 for i in range(5) for j in range(4) }

That will build a grid indexed by 2-tuples.

{(0, 1): 0, (1, 2): 0, (3, 2): 0, (0, 0): 0, (3, 3): 0, (3, 0): 0, (3, 1): 0, (2, 1): 0, (0, 2): 0, (2, 0): 0, (1, 3): 0, (2, 3): 0, (4, 3): 0, (2, 2): 0, (1, 0): 0, (4, 2): 0, (0, 3): 0, (4, 1): 0, (1, 1): 0, (4, 0): 0}
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 7
    I'll give you +1 if you share the research that led to that value of 80%. – Niklas B. Feb 27 '12 at 02:38
  • 9
    It's the Pareto Principle. http://en.wikipedia.org/wiki/Pareto_principle. 80% of the time, you only need 20% of the data structures. – S.Lott Feb 27 '12 at 02:49
  • The same isn't exactly true for primitive type. The outer "4" is creating 4 references that are pointing to the same list, but the inner "5" isn't creating 5 references that are pointing to the same "None". So you can still do [[None] * 5 for j in range(4)] – qkhhly Sep 26 '14 at 15:28
  • 2
    @qkhhly: No, it behaves exactly the same for all types. You can confirm that all references point to the same None by looking at `[[id(x) for x in sublist] for sublist in q]`. – DSM Nov 11 '15 at 05:36
  • Using list comprehension `[[x for x in mylist] for x in range(9)]` didn't help me in case of list of lists. Solved the problem using `[[x for x in copy.deepcopy(mylist)] for x in range(9)]` instead. – Semen Feb 24 '21 at 10:07
14

The reason why is you have the list, just duplicated four times! Python isn't regenerating that list every time when you do *4. It's using the same list object.

To get around this, you need for force python to regenrate that list for you every time:

[ [None] * 5 for i1 in range(4) ]

In this case, I'm using a list comprehension.

Donald Miner
  • 38,889
  • 8
  • 95
  • 118
9

q = [[None]*5]*4 print(q) q[0][1]=4 print(q)

print('right solution') q = [ [ None for i in range(5) ] for j in range(4) ] q[1][1]=4 print(q)

result :

[[None, None, None, None, None], [None, None, None, None, None], [None, None, None, None, None], [None, None, None, None, None]] [[None, 4, None, None, None], [None, 4, None, None, None], [None, 4, None, None, None], [None, 4, None, None, None]] right solution [[None, None, None, None, None], [None, 4, None, None, None], [None, None, None, None, None], [None, None, None, None, None]]

Jin Thakur
  • 2,711
  • 18
  • 15
4

The answer is simple Never use

q = [[None]*5]*4

as when you do assignment

q[0][1]=5 it assigns value multiple time to multiple rows at 1 column try print(q)

rather use

q = { (i,j):0 for i in range(5) for j in range(4) }

then q[0][1]=5 will assign one time only try

print(q)
Anthon
  • 69,918
  • 32
  • 186
  • 246
Jin Thakur
  • 2,711
  • 18
  • 15
3

Why is Python initializing the first value of every array and not just the first one?

Because they are the same array, referred to multiple times.

Is there a better way to accomplish what I'm trying to do?

Create the structure such that the outer array refers to separate inner arrays instead of reusing one. The other answers provide ways to do so.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
2

The answer to question number 2: Using numpy is an option. See following code.

import numpy as np

# creating 2D array with nans
num_of_rows = 5
num_of_cols = 3
a = np.full((num_of_rows, num_of_cols), np.nan) 
#for zero vals: a = np.zeros((num_of_rows, num_of_cols))

# placing number 5 in row 3, col 1
value = [5]
position_row = 3
position_col = 1
# the put command below flattens the 2D array
position = [int(num_of_cols * position_row + position_col)] 
np.put(a, position, value)

result:

[[ nan  nan  nan]
 [ nan  nan  nan]
 [ nan  nan  nan]
 [ nan   5.  nan]
 [ nan  nan  nan]]
SDJ
  • 21
  • 4
0

In case you want to use a list and not a dictionary as the others propose you can use this:

q[0] = [5,None,None,None,None]
manosbar
  • 318
  • 2
  • 3
  • 15