# populating a doubly-subscripted array

Discussion in 'Python' started by ghw@accutrol.com, Nov 8, 2010.

1. ### Guest

What am I missing? I am using Python 3.1.2.

ff = [[0.0]*5]*5
ff #(lists 5x5 array of 0.0)
for i in range(5):
for j in range(3):
ff[j] = i*10+j
print (i,j,ff[j]) # correctly prints ff array values

ff # try this and see what happens!

result of first print of ff
[[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]]

result of second print of ff
0 0 0
0 1 1
0 2 2
1 0 10
1 1 11
1 2 12
2 0 20
2 1 21
2 2 22
3 0 30
3 1 31
3 2 32
4 0 40
4 1 41
4 2 42

result of third print of ff
[[40, 41, 42, 0.0, 0.0], [40, 41, 42, 0.0, 0.0],
[40, 41, 42, 0.0, 0.0], [40, 41, 42, 0.0, 0.0],
[40, 41, 42, 0.0, 0.0]]

Obviously there is something missing in my understanding of
how array values are populated. Why do all "rows" get
populated with the same set of values (40, 41, 42)?

Thanks for your help,
Hamilton Woods

, Nov 8, 2010

2. ### Mark WoodingGuest

writes:

> What am I missing? I am using Python 3.1.2.
>
> ff = [[0.0]*5]*5
> ff #(lists 5x5 array of 0.0)
> for i in range(5):
> for j in range(3):
> ff[j] = i*10+j
> print (i,j,ff[j]) # correctly prints ff array values
>
> ff # try this and see what happens!
>
> result of third print of ff
> [[40, 41, 42, 0.0, 0.0], [40, 41, 42, 0.0, 0.0],
> [40, 41, 42, 0.0, 0.0], [40, 41, 42, 0.0, 0.0],
> [40, 41, 42, 0.0, 0.0]]
>
> Obviously there is something missing in my understanding of
> how array values are populated. Why do all "rows" get
> populated with the same set of values (40, 41, 42)?

[I suppose that the trailing 0.0 entries aren't in question: they're
because the individual rows have width 5 -- from `[0.0]*5' -- but only
the first three items of each is initialzed -- because j iterates only
over range(3).]

So, why do they all come out the same? The answer is because in fact
there are only two lists involved. You probably thought that you were
building something like the thing on the left. In fact, you have the
thing on the right. (Try to forgive the rubbish ASCII art.)

.--. .--.--.--.--.--. .--.
| ----->| | | | | | | --.
|__| `--^--^--^--^--' |__| \
| | .--.--.--.--.--. | | \
| ----->| | | | | | | ---. \
|__| `--^--^--^--^--' |__| \v
| | .--.--.--.--.--. | | \.--.--.--.--.--.
| ----->| | | | | | | ----->| | | | | |
|__| `--^--^--^--^--' |__| /`--^--^--^--^--'
| | .--.--.--.--.--. | | / ^
| ----->| | | | | | | --' /
|__| `--^--^--^--^--' |__| /
| | .--.--.--.--.--. | | /
| ----->| | | | | | | --'
`--' `--^--^--^--^--' `--'

What [x] * n does is make a list, whose length is n, and all of whose
entries are precisely the value x. If x itself is a list, then you get
an outer list all of whose entries are the /same/ inner list -- not
copies of it. So when you appear to change entries in one of the inner
lists, all the other inner lists seem to change too -- because they're
actually the same list.

-- [mdw]

Mark Wooding, Nov 8, 2010

3. ### Mark WoodingGuest

Gregory Ewing <> writes:

> A reasonably elegant way to fix this is to use list comprehensions
> for all except the innermost list:
>
> ff = [[0.0]*5 for i in xrange(5)]

Yes, this is a good approach. I should have suggested something like
this as a solution myself, rather than merely explaining the problem.

> If you're computing heavily with arrays, you might also consider
> using numpy arrays instead of lists.

Also a good idea.

-- [mdw]

Mark Wooding, Nov 9, 2010