0

I've tried changing a single value in a simple 2d array after initializing it but instead, it changed the column. But the same thing is working if I do it the other way. Why is that?

>>> arr=[[None]*4]*4
>>> arr
[[None, None, None, None],
 [None, None, None, None],
 [None, None, None, None],
 [None, None, None, None]]

# My problem
>>> arr[1][2]=0
>>> arr
[[None, None, 0, None],
 [None, None, 0, None],
 [None, None, 0, None],
 [None, None, 0, None]]

# But it works like this
>>> arr=[[None for _ in range(4)] for _ in range(4)]
>>> arr[1][2]=0
>>> arr
[[None, None, None, None],
 [None, None, 0, None],
 [None, None, None, None],
 [None, None, None, None]]
Adam.Er8
  • 12,675
  • 3
  • 26
  • 38
Amogh Bharti
  • 61
  • 1
  • 3

3 Answers3

2

when you multiply a list, each element is a reference to the same object in-memory.

in the second method, you create a new object for each row.

you can compare identity using is, and see the difference:

>>> arr=[[None]*4]*4
>>> arr[0] is arr[1]
True
>>> arr=[[None for _ in range(4)] for _ in range(4)]
>>> arr[0] is arr[1]
False
>>> 

From the docs:

The operators is and is not test for an object’s identity: x is y is true if and only if x and y are the same object. An Object’s identity is determined using the id() function. x is not y yields the inverse truth value.

Adam.Er8
  • 12,675
  • 3
  • 26
  • 38
1

Your can identify it by using id() from Built-in Functions in Python. This is the explanation of id().

Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.

So id() is use for get location of the object in memory. When you create any variable it crates on memory, so the variable is referenced to that object. You can get the id of that variable by passing your variable into id() function. You can refer more about from What is the id( ) function used for? question.

Let's look at what happen in your program.

>>> arr=[[None]*4]*4
>>> id(arr)
2338171271624  # whole object reference to this id in the memory
>>> id(arr[0])
140719057423584  # every sub list is referenced to the same memory id
>>> id(arr[1])
140719057423584
>>> id(arr[2])
140719057423584
>>> id(arr[3])
140719057423584

According to this example whole object is reference to 2338171271624 and every sub list is referenced to the memory object of 140719057423584. Which mean once you change the value of any sub list, it appear on other sub lists too. That's why you assign arr[1][2]=0 this value and change all the 3rd columns in every sub list.

Let's do some experiment with your 2nd example.

>>> arr=[[None for _ in range(4)] for _ in range(4)]
>>> id(arr)
2338171052168  # I change the previous value and created new reference
>>> id(arr[0])
2338169897864
>>> id(arr[1])
2338172707016
>>> id(arr[2])
2338172885768
>>> id(arr[3])
2338172960456

As you can see all the sub list id are differ from other sub lists. So when you change any value of the sub list, it available only for that specific sub list.

Kushan Gunasekera
  • 7,268
  • 6
  • 44
  • 58
0

That is absolutely correct, coz the * operation on the list doesn't create a new list rather it uses the existing list and iterates over. So basically here each row of the matrix is referencing to the same list hence updating any element of a row will result in the update of the entire column.

row = [None]*4 #Results to a list [None, None, None, None]
arr = [row]*4 #Results to 2-D matrix with same repeated row
Kaushal Pahwani
  • 464
  • 3
  • 11