'Iterating and modifying a list of numpy arrays leave arrays unchanged [duplicate]

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?



Solution 1:[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]))

Solution 2:[2]

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.

Solution 3:[3]

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

Solution 4:[4]

It can be like this:

interface IInfoList<T> {
  title: string;
  arr: {
    //want the key here to be generic
    key: T;
  }[];
}

// extends unknown it's a hack to make the linter happy
export const InfoList = <T extends unknown>(props: IInfoList<T>) => {
  return (
    <Row className="align-items-start">
      <Col lg={1}>
        <h5>{props.title}: </h5>
      </Col>
      <Col>
        {props.arr.slice(0, 6).map((item, i) => {
          //Generic key accessed via "item" here
          return (
            <p className="pr-1 mb-0 d-inline" key={i}>
              {' '}
              {`${item.key}, `}
            </p>
          );
        })}
      </Col>
    </Row>
  );
};

Then usage

const Parent = () => {
  return (
    <>
      <InfoList<number>
        title="Title"
        arr={[
          {
            key: 1,
          },
        ]}
      />
      <InfoList<string>
        title="Title"
        arr={[
          {
            key: '1',
          },
        ]}
      />
    </>
  );
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Anish Shilpakar
Solution 3
Solution 4 Oro