0

I would like to iterate through all elements of a matrix (in my code a list of lists) and create an independent copy of that matrix everytime, when the checked element meets a certain condition.

Every time a copy is created, I would like to change one of the elements in the copied matrix (so that the original matrix stays the same). Every copy of the matrix should get an individual name.

After that, I would like to store that copied matrix in a list.

So, for example: Consider the original Matrix is a 2x2 Matrix, containing four integers (let's say the numbers 1 to 4, as shown in the code below). Now let's loop through the matrix elements and create a copy of the matrix everytime, when the checked element is larger than 3. So we should get one copy (because only one element, the number 4, is larger than 3). In this copied matrix, I change one of the elements (e.g. let's say adding the number 10 to the element that was checked). Then I store this copied matrix in a list. My code looks like this:

matrix = [[1,2],[3,4]]
new_copies = []
counter = 0

for i in range(0,2):
    for k in range(0,2):
        if matrix[i][k] > 3:
            exec("item%s = matrix[:]" % counter)
            exec("item%s[i][k] = matrix[i][k] + 10" % counter)
            exec("new_copies.append(item%s)" % counter)
            counter += 1

print(matrix)
print(new_copies)

If you run this code, you will see that the copied matrix is changed correctly and also is stored in the list.

But the original matrix also is changed. Why? I only manipulate the copied versions of the matrix, which should be independent from the original, since I follow this principle:

new_matrix = original_matrix[:]
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Colodore
  • 3
  • 2
  • A slice is a *shallow* copy, if you have nested lists you need a deep copy. Also using exec like that is totally unnecessary, use a dictionary or list not dynamic variables (see https://stackoverflow.com/questions/1373164/how-do-i-create-a-variable-number-of-variables). – jonrsharpe Jan 19 '19 at 17:43
  • 1
    Why do you use `exec`? – Austin Jan 19 '19 at 17:44
  • At the very least, your `exec` should be replaced by `getattr` or a lookup into `globals()`. – iBug Jan 19 '19 at 17:53

1 Answers1

0

Why it is happening

Lists are mutable objects, that is why even if you are creating a new list object by doing matrix[:], your sublists are still pointing to the same objects...

A first solution

Here is a first workaround:

matrix = [[1, 2], [3, 4]]
new_copies = []
counter = 0

for i in range(0, 2):
    sublist = matrix[i][:]

    for k in range(0, 2):
        if matrix[i][k] > 3:
            sublist[k] += 10
            counter += 1

    new_copies.append(sublist)

print(matrix)
print(new_copies)

Or with lists comprehension

If possible, you could also use list comprehension, in this case that would be:

new_copies = [[(e + 10 if e > 3 else e) for e in l] for l in matrix]

which will give you the same result that my previous proposition

Or with mutable objects

A nice solution would be to use tuple instead of lists, because they are immutable objects. But it won't be possible if you have to modify your matrix along your program.

Or with deepcopy

You could also use the deepcopy method from the copy library...

And you saw me coming...

I have to remind that the use of eval should be avoided if possible...

olinox14
  • 6,177
  • 2
  • 22
  • 39
  • Thank you so much for this detailed answer! I used deepcopy and now everything works fine. Perfect! – Colodore Jan 20 '19 at 13:00