0

Consider the two following codes:

import numpy as np

mainlist = [np.array([0,0,0, 1]), np.array([0,0,0,1])]

for i in range(len(mainlist)):
    mainlist[i] = mainlist[i][0:2]

print(mainlist) # [array([0, 0]), array([0, 0])] => OK!

and:

import numpy as np

mainlist = [np.array([0,0,0, 1]), np.array([0,0,0,1])]

for element in mainlist:
    element = element[0:2]

print(mainlist) # [array([0, 0, 0, 1]), array([0, 0, 0, 1])] => WTF?

I was wondering why, in the second case, the arrays remain unchanged. It does not even throw an error about mutability problems. Could you explain exactly what is going on regarding the behavior of the second code? What would be the right way of doing it instead?

Vincent
  • 57,703
  • 61
  • 205
  • 388
  • 2
    This is bound to be marked as a duplicate, but from my limited understanding, Python attempts to always use reference to the original variable until it cannot. One of the operations breaking the reference is assignment where Python just does a copy of the variable and continues. You do this assignment in loop so each assignment does a copy of the `element` thus the `mainlist[i]` is not changed in the list. – vahvero May 19 '22 at 12:00
  • The second case is not mutating anything. It just assigns a new value to a variable. – hpaulj May 19 '22 at 15:47

4 Answers4

1

element is holding a reference to the array (since you are iterating the list which is also just storing references to the arrays) and not a pointer to the list. element = element[0:2] is just changing the reference stored in element, leaving the one in the list unchanged. You can check the identity of the referenced object using id():

import numpy as np

mainlist = [np.array([0,0,0, 1]), np.array([0,0,0,1])]

ref_0 = id(mainlist[0])

for element in mainlist:
    element = element[0:2]

print(mainlist) # [array([0, 0, 0, 1]), array([0, 0, 0, 1])] => WTF?

# True: referenced object changed.
print(ref_0 == id(mainlist[0]))

By doing manlist[i] you are actively changing the reference in the list stored at position i to the new view into the array:

import numpy as np

mainlist = [np.array([0,0,0, 1]), np.array([0,0,0,1])]

ref_0 = id(mainlist[0])

for i in range(len(mainlist)):
    mainlist[i] = mainlist[i][0:2]

print(mainlist) # [array([0, 0]), array([0, 0])] => OK!

# False: referenced object changed.
print(ref_0 == id(mainlist[0]))
user2246849
  • 4,217
  • 1
  • 12
  • 16
1

Variable name a --->(point to) np.array([0,0,0, 1])

Variable name b --->(point to) np.array([0,0,0, 1])

Variable name mainlist --->(point to) [a, b]

When you use for element in mainlist:,

Variable name element --->(point to) np.array([0,0,0, 1])


When you assign another value to element by element = np.array([]),

element --->(point to) np.array([])

But the mainlist is still pointing to [a, b].


When you use mainlist[0] = np.array([]), you really put np.array([]) on the first place of mainlist, but the a is still pointing to np.array([0,0,0, 1]).

blueice
  • 114
  • 5
0

In the first case, you are actually modifying the elements of the list using the index position, so the list is updated, but in the second case you are only taking the element of the list and then creating a new variable called element and updating element but not updating the actual values present inside the list. So, the original list remains unchanged.

If you want to use the second method to update the list values, you can create a second list and append the element in that list.

0

Python operates on reference. In the second example, element is a variable of the loop referencing a list item (ie. a Numpy array). element = element[0:2] causes element to reference another object: a newly created view object. element is set later to another reference and the temporary view is implicitly deleted. It does not mutate the initial mainlist list, but only the element reference. In the first example, mainlist[i] = mainlist[i][0:2] mutates mainlist[i] and so the original mainlist list. The item is modified to reference a newly created view. mainlist[i] is not overwritten later so you see the change.

Another example to show such problem is:

l = [[None]*2]*2  # [[None, None], [None, None]]
l[0][0] = 42      # [[42, None], [42, None]]

Two items are modified since they reference the same object (ie. the list [42, None]). You can see that using id: id(l[1]) == id(l[1]).

Jérôme Richard
  • 41,678
  • 6
  • 29
  • 59