0

I'm trying to create a long list, without having to manually type it out every time. I'm using this currently (replaced longer number with 0 for simplicity):

mylist = [[0]*5]*5

Which is:

[[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]]

Printing each row, this list looks as follows:

00000
00000
00000
00000
00000

When I try to change one of the numbers (let's say first row and first item), it sets all of the rows' first item.

mylist[0][0] = 1

Printing each row:

10000
10000
10000
10000
10000

How do I set only the item I want?

Zach Gates
  • 4,045
  • 1
  • 27
  • 51

2 Answers2

3

You're seeing this behaviour because you've created a list, mylst, with five copies of the same list inside it.

You can see this by using id, which returns a unique identifier for an object:

In [1]: lst = [[0]*5]*5

In [2]: [id(sublst) for sublst in lst]
Out[2]: 
[139951176432584,
 139951176432584,
 139951176432584,
 139951176432584,
 139951176432584]

This is because [0]*5 gives you a list with five zeros, but then multiplying it by five gives you five copies of that same list, not five different lists with the same value.

You can avoid this behaviour by using a list comprehension to initialise your original list:

In [3]: lst = [[0]*5 for _ in range(5)]

In [4]: [id(sublst) for sublst in lst]
Out[4]: 
[139951176420808,
 139951176419528,
 139951176419464,
 139951176419592,
 139951176324040]

As a matter of style, I prefer to avoid using * to create anything but simple flat lists. For the sake of consistency, then, I'd probably create your list initially with:

In [3]: lst = [[0 for _ in range(5)] for _ in range(5)]

The advantage of this approach is that you don't need to change anything if later on, instead of 0, you want to initialise the values to a custom object (say, Banana()).

Without this approach, you can run into the same issue as before, except now it will be the object in the inner list which is repeated:

In [5]: class Banana(object):
   ...:     pass
   ...: 

In [6]: lst = [[Banana()]*5 for _ in range(5)]

In [7]: [[id(elem) for elem in sublst] for sublst in lst]
Out[7]: 
[[139951176414824,
  139951176414824,
  139951176414824,
  139951176414824,
  139951176414824],
 [139951176415272,
  139951176415272,
  139951176415272,
  139951176415272,
  139951176415272],
 [139951176414096,
  139951176414096,
  139951176414096,
  139951176414096,
  139951176414096],
 [139951176414432,
  139951176414432,
  139951176414432,
  139951176414432,
  139951176414432],
 [139951176415496,
  139951176415496,
  139951176415496,
  139951176415496,
  139951176415496]]

In other words, the only reason that 0 works at all, in the flat list or otherwise, is that it's not a problem to have multiple copies of the same int object (along with other builtin types such as float), as those objects are treated as immutable and compared by value.

sapi
  • 9,944
  • 8
  • 41
  • 71
  • 1
    "is that certain builtin objects (such as int and str) will be copied by value, not reference": no, all objects, regardless of their type, behave exactly the same way with regards to multiplication of the container. – DSM Jan 27 '15 at 03:27
  • @DSM Good point. I was trying to make a point about the observable behaviour being different, but you're right, I misspoke. – sapi Jan 27 '15 at 03:28
  • With your Banana example, `lst[0][0] = Banana()` will always behave the same as any int or str examples, because item assignment in a list mutates the list but not the item. Augmented assignment can behave differently - eg, `lst[0][0] += addable` can modify the list item if it is mutable (which strs and ints aren't). This has nothing to do with built-in types having different rules (they don't) and everything to do with the behaviour of the relevant classes' `__i*__` methods. – lvc Jan 27 '15 at 03:45
  • @lvc I was more thinking that doing something like lst[0][0].mutating_method() might lead to unexpected results. It's a different problem to that in the OP, but it's related enough that I thought it was with raising. – sapi Jan 27 '15 at 03:47
  • 1
    While that is certainly relevant (and *exactly* the OPs issue - where `mutating_method` is `lst.__setitem__`), it also isn't a copy by value vs copy by reference issue - it's just that ints have no mutating methods. – lvc Jan 27 '15 at 03:57
0

You shouldn't contrusct list using the * operator, it sets the same item to all the list.

Use

mylist = [[0 for n in range(5)] for _ in range(5)]
mylist[0][0] = 1

print(mylist)

Output:

[[1, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
f.rodrigues
  • 3,499
  • 6
  • 26
  • 62