Reputation: 83
I create a matrix with 4 lines and 5 columns filled with zeros. Then i put some values into the first line (line 0) and want to calculate values for the second line (line 1) with a function. My code is:
x = [0,1,2,1,0]
u=[[0]*(len(x))]*(4)
u[0]=x
for n in range(0,1):
print u
for j in range(1,4):
u[n+1][j]=u[n][j]-(u[n][j+1]-u[n][j-1])
print 'Value %.f at position %.f %.f' %(u[n+1][j],n+1,j)
print u
But the results of prints is:
[[0, 1, 2, 1, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
Value -1 at position 1 1
Value 2 at position 1 2
Value 3 at position 1 3
[[0, 1, 2, 1, 0], [0, -1, 2, 3, 0], [0, -1, 2, 3, 0], [0, -1, 2, 3, 0]]
I don't understand why the program calculates values for the line 1 and columns 1, 2 and 3 but after the loop lines 2 and 3 are also with values. I expect the results of prints was:
[[0, 1, 2, 1, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
Value -1 at position 1 1
Value 2 at position 1 2
Value 3 at position 1 3
[[0, 1, 2, 1, 0], [0, -1, 2, 3, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
Upvotes: 0
Views: 1101
Reputation: 61
This question has been answered before multiple times, such as here and here
The short story is that when you multiply [0]*(len(x))
you get a list with 5 elements referencing the number 0. Let's call this list "Joe". The rest of your expression creates another list with a reference to "Joe" in it, which is multiplied 4 times, resulting in a list with 4 references to the same "Joe"(list). You think you have 20 values when in fact you only have 10: 1 the value for the number 0, 5 values in a list, all holding the reference to the number 0, 4 values in the outermost list, all holding the reference to the list with 5 values. (1+5+4=10, maybe this would make a better sense as a diagram)
I'm going to attempt to break it down further, specifically for your code:
(We'll try to clean up your expression a bit from u=[[0]*(len(x))]*(4)
to u=[[0]*5]*4
)
This can then be expanded into:
element=[0]
line=[element*5]
u=line*4
The key point is that when we print line
we will get
[[0, 0, 0, 0, 0]]
and this means
u=line*4
is the same as
u=[[0, 0, 0, 0, 0]]*4`
so when you print u
you get the expected
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
Now, when you modify one of the "cells", say the third one on the third line
u[2][2]=1
You get
[[0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0]]
You may say that's odd and inconsistent: both element
and line
are lists, how come values are only copied "vertically"?
This oddity is due to fact that in Python containers (such as lists) only use references:
Every object has an identity, a type and a value. An object’s identity never changes once it has been created; you may think of it as the object’s address in memory.
Some objects contain references to other objects; these are called containers. Examples of containers are tuples, lists and dictionaries. The references are part of a container’s value. (but the actual values of the referenced items are not)
(see more in Python's data model)
....and you can think of the * operator as something like this:
def multiply(list, times):
result = []
for i in range(times):
value=list[i % len(list)]
result.append(value)
return result
with your code rewritten as
u = multiply([multiply([0],5)],4)
Upvotes: 1