5

I just met something really strange of Python:

>>> out=[[0]*3]*3
>>> out
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> out[0][1]
0
>>> out[0][1]=9
>>> out
[[0, 9, 0], [0, 9, 0], [0, 9, 0]]

well, obviously, what I want is :

[[0, 9, 0], [0, 0, 0], [0, 0, 0]]

isn't strange? I'm not very familiar with Python, but Python always impresses me with its intuitive behavior. But how it comes up with this?
... and how can I get what I need?

thanks!

Watt

Matt
  • 741
  • 1
  • 6
  • 17
  • You're right about one thing - it's not very intuitive. I don't think they fixed it in Python 3 either. – Mark Ransom Jun 07 '12 at 23:06
  • @MarkRansom Why do you think it is something that needs to be **fixed**? What if you actually did want this behaviour, how would that be achieved if it was changed? – jamylak Jun 07 '12 at 23:11
  • 1
    For anyone wondering if people actually use this behavior correctly in code, see the the documentation on [`zip()`](http://docs.python.org/library/functions.html#zip), which has an example for clustering sequences into n-length groups using `zip(*[iter(s)]*n)`. – Andrew Clark Jun 07 '12 at 23:20
  • yes, the problem is only about the first line. And it is somehow intuitive too now. – Matt Jun 07 '12 at 23:21
  • I know this is a duplicate of many, many other questions, but I'm somehow failing to find a good example. – Karl Knechtel Jun 07 '12 at 23:30
  • @jamylak: `inner=[0,0,0]` `out=[inner for i in range(3)]` Let the usual case work as expected and the oddball case require extra work. As it is the `*3` notation is almost worthless, there are few times when you want what it gives you. – Mark Ransom Jun 08 '12 at 03:06
  • @Matt, there's a definite logic to it and once bitten you'll never forget. The problem is that *everyone* gets bitten. – Mark Ransom Jun 08 '12 at 03:07
  • 1
    @KarlKnechtel, my first thought was "oh no not again." Sometimes it's just too much trouble to hunt down the duplicates. – Mark Ransom Jun 08 '12 at 03:08

2 Answers2

9

A strange behaviour indeed, but that's only because * operator makes shallow copies, in your case - shallow copies of [0, 0, 0] list. You can use the id() function to make sure that these internal lists are actually the same:

out=[[0]*3]*3
id(out[0])
>>> 140503648365240
id(out[1])
>>> 140503648365240
id(out[2])
>>> 140503648365240

Comprehensions can be used to create different lists as follows:

out = [ [0]*3 for _ in range(3) ]
Zaur Nasibov
  • 22,280
  • 12
  • 56
  • 83
8

Using * to duplicate elements in lists is a shallow copy operation, so you will end up with multiple references to the same mutable objects if you use this on a list that contains mutable objects.

Instead, use the following to initialize your nested list:

out = [[0]*3 for _ in range(3)]

You can see that with your method, each entry in out is actually a reference to the same list, which is why you see the behavior that you do:

>>> out = [[0]*3]*3
>>> out[0] is out[1] is out[2]
True
Andrew Clark
  • 202,379
  • 35
  • 273
  • 306