2

I followed the two following link to solve my issue: first, second

My script is below:

list0=[[0,1,2],[3,4,5],[5,6,7]]
list2=list(list0)
list2[0][0]=20
print(list0) # displays [[20, 1, 2], [3, 4, 5], [5, 6, 7]]
print(list2) # displays [[20, 1, 2], [3, 4, 5], [5, 6, 7]]

As you can see, modifying my list2 modify as well list0. However I took care to create list2 with the function list(). Shouldn't it create a really new list?

I don't understand why it doesn't work here.

On the other hand, it this other example below it works:

list0=[[0,1,2],[3,4,5],[5,6,7]]
list2=list(list0)
list2[0]=[20,30,40]
print(list0) # prints [[8, 9, 10], [3, 4, 5], [5, 6, 7]]
print(list2) # prints [[20, 30, 40], [3, 4, 5], [5, 6, 7]]: Good !

My questions:

Why it doesn't work in the first example but does in the second? How to make sure to not have this issue and to "really" create a new list such that if I modify this new the old will never be modified, whatever there is in this old list.

Is it because list2 and list0 are pointing toward different elements. Thus modifying list2[i] will not modify list0[i]. However, list2[i] and list0[i] point toward the same list, hence modifying list2[i][j] will modify list0[i][j]? In some way I created different references at some hierarchy but not for all levels?

StarBucK
  • 209
  • 4
  • 18
  • 1
    You are modifying the list *inside of* ``list0``/``list2``, but never copied it. – MisterMiyagi Dec 02 '21 at 12:30
  • 1
    Elaborating Mr. Miyagi comment, `l2` is a new list that at the beginning contains the same lists contained in `l0`: if you modify one of the inner lists, the modification is reflected in both the outer lists.. On the contrary, if, e.g., you append a new item to `l0` you'll verify that `l2` is left unmodified – gboffi Dec 02 '21 at 12:34
  • @gboffi Thank you for the comment. How can I make sure to never have this problem whatever the list contains with the simplest code possible (I wan't to be sure that whatever level I modify elements in list2 it will never affect list0). Also to be sure to understand: list2 and list0 now points toward different elements. But list2[i] and list0[i] points toward the same, hence it is what is causing the issue. Is it what you meant? – StarBucK Dec 02 '21 at 12:39
  • @StarBucK I see you have accepted the right answer, no need for further comments... – gboffi Dec 02 '21 at 13:40

2 Answers2

2

The problem here is that while list2 = list(list0) makes it so that the outer list is different between your two instances, all inner lists are still the same. To avoid this you can either use copy.deepcopy:

from copy import deepcopy

list0 = [[0, 1, 2], [3, 4, 5], [5, 6, 7]]
list2 = deepcopy(list0)
list2[0][0] = 20
print(list0)  # [[0, 1, 2], [3, 4, 5], [5, 6, 7]]
print(list2)  # [[20, 1, 2], [3, 4, 5], [5, 6, 7]]

or a nested list comprehension:

list0 = [[0, 1, 2], [3, 4, 5], [5, 6, 7]]
list2 = [list(sublist) for sublist in list0]
list2[0][0] = 20
print(list0)  # [[0, 1, 2], [3, 4, 5], [5, 6, 7]]
print(list2)  # [[20, 1, 2], [3, 4, 5], [5, 6, 7]]

but do note that you'd have to adjust the latter approach if the nesting depth was different.

osego
  • 138
  • 1
  • 5
  • 1
    Thank you for your answer. In summary can I say that deepcopy creates really new list at all levels while using copy or doing the trick I did only works for the first level. Deepcopy might be slower but will always work to create new references at all levels, no matter how much levels there are in the list. Would you agree? – StarBucK Dec 02 '21 at 12:47
  • 1
    Yes, this notion is more or less correct, [per documentation](https://docs.python.org/3/library/copy.html): shallow copy creates a new compound object and inserts *references* to objects found in the original, deep copy creates new compound object and *recursively* inserts *copies* of objects found in the original. The documentation does not explicitly mention performance difference but `deepcopy` is indeed significantly slower, even more so for instances of custom classes with no `__deepcopy__` dunder method. – osego Dec 02 '21 at 13:16
-2

No, list() converts it's parameter to a list. Therefore you're having list2 as variable which references list0. What you're looking for is copy().

white
  • 601
  • 3
  • 8
  • ``list2`` does *not* reference ``list0``. They both reference the same contained lists, though. – MisterMiyagi Dec 02 '21 at 12:40
  • Isn't a reference on a reference a reference to the original data anyway? – white Dec 02 '21 at 12:45
  • ``list2`` and ``list0`` are completely separate objects. For example, you can ``del list0[1]`` without changing ``list2`` in any way. Both have *separate* references to the same content, which means modifying one such reference does not impact the other. – MisterMiyagi Dec 02 '21 at 12:50
  • Ok, makes sense with the `del()` argument. So `list2` saves its own reference to the original object by following the reference from `list0` then? – white Dec 02 '21 at 13:00
  • ``list2`` contains all the objects that ``list0`` *contained* at the time ``list2`` was created. You could say that means it stores copies of the references in ``list0``. – MisterMiyagi Dec 02 '21 at 13:04