-1

Asking out of curiosity. For the sake of making a point I was trying to make a function that returns an "identity matrix" of n dimensions and then printing it in the most concise way. First I came up with this:

def identity(n):
    zeros = [[0 for j in range(n)] for i in range(n)]
    for i in range(n):
        zeros[i][i] = 1
    return zeros

for i in range(5):
    print(identity(5)[i])

This works as intended, however then I tried making the syntax shorter by doing this:

def identity(n):
    zeros = [[0]*n]*n
    for i in range(n):
        zeros[i][i] = 1
    return zeros

for i in range(5):
    print(identity(5)[i])

And this for some reason changes every single element to a one, but I can't seem to figure out why?. This isn't an important question but help is much appreciated!

KevinQ
  • 1
  • Note that if you're doing any kind of linear algebra, NumPy and SciPy are your friends. NumPy has a function to create an identity matrix of a given size, for example. – ndc85430 Jun 22 '22 at 04:46

3 Answers3

1

lists are kept by reference in python. This means that if you have:

list_a = [1,2,3,4]
list_b = list_a

list_a and list_b are actually pointing to the same object in memory. so if you change an element of list_a:

list_a[2] = 9

then the same element for list_b will change because they are pointing to the same object. i.e. list_a literally equals list_b in every way.

That's what's happening in your code as well. When you loop through and assign each value then it's as if you were explicitly creating a new list and assigning it to each element of your outter list:

l = []
l.append([1,2,3,4])
l.append([1,2,3,4])
...

but in the second piece of code, it is as if you are repeatedly appending the same value to the list:

l = []
la = [1,2,3,4]
l.append(la)
l.append(la)
Amir
  • 129
  • 4
0

It's because list comprehension performs shallow copy on the element.
All elements in zeros are refering to the same list.
Try these and see the results:

n = 4
zeros = [[0]*n]*n
zeros[0] = 1
print(zeros)
n = 4
lst = [0]*n
zeros = [lst]*n
print(zeros)

lst[0] = 1
print(zeros)

You can learn more about difference between shallow and deep copy here.

WieeRd
  • 792
  • 1
  • 7
  • 17
-1

This is because when you multiply a list with a number, it duplicates it, but it does so by copying the reference of each element.

If you do id(zeros[0][0]) and id(zeros[0][1]) for the second case, you will see that they are the same, i.e. they refer to the same element and modifying one modifies them all.

This is not true for the first case where every 0 is instantiated seperately and has its own id.

Edit:

def zeros(n):
    return [[0]*n]*n
x = zeros(5)
for i in range(5):
    for j in range(5):
        assert id(x[0][0]) == id(x[i][j])
Niteya Shah
  • 1,809
  • 1
  • 17
  • 30