0

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

1 Answer 1

1

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)
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.