0

It is quite a silly question, but I am really confused. Please have a look my code:

>>> my_list = [1, 2, 3]
>>> my_list_new = my_list[:]
>>> my_list_new[0] = 100
>>> my_list_new
[100, 2, 3]
>>> my_list
[1, 2, 3]

So it works as it should. I copied my_list. When I changed the my_list_new - only one list changed.

Now look here:

>>> my_list2 = [[1, 2, 3], [4, 5, 6]]
>>> my_list_new2 = my_list2[:]
>>> my_list_new2[0][0] = 100
>>> my_list_new2
[[100, 2, 3], [4, 5, 6]]
>>> my_list2
[[100, 2, 3], [4, 5, 6]]

As you can see I changed my_list_new2, but both lists changed. Is it normal Python behaviour for nested lists? How to avoid it?

DavidG
  • 24,279
  • 14
  • 89
  • 82
Trarbish
  • 363
  • 4
  • 16
  • There are two good answers below. To learn more check here: https://docs.python.org/2/library/copy.html#module-copy You need to do a `deepcopy` if the list is nested, otherwise only the references to the nested list are copied. – joel goldstick Nov 22 '16 at 15:32
  • It looks like you know that you need to use the slice syntax to avoid copying only the reference, so I'm not sure why you're surprised that lists "copied" without that syntax - namely, the inner ones - were only copied by reference. – TigerhawkT3 Nov 22 '16 at 15:42

4 Answers4

1

This should work as intended

my_list_new2 = [list[:] for list in my_list2]

Proof

my_list2 = [[1, 2, 3], [4, 5, 6]]
my_list_new2 = [list[:] for list in my_list2]
my_list_new2[0][0] = 100

my_list_new2
[[100, 2, 3], [4, 5, 6]]

my_list2
[[1, 2, 3], [4, 5, 6]]
Richy
  • 380
  • 2
  • 10
1

List are mutable in python. Meaning:

a = [1,2,3]
b = a
a[0] = 4
print(b)

Will print [4, 2, 3]

So in case of nested lists you only copy the "outer" list, and modify the inner list, so you will modify the other list as well. If you are interested in this look up list pointers in python

Solution:

my_list2 = [[1, 2, 3], [4, 5, 6]]
my_list_new2 = [inner_lst.copy() for inner_lst in my_list2] # You can use inner_lst[:] as well
my_list_new2[0][0] = 100
print(my_list2)

Returns: [[1, 2, 3], [4, 5, 6]]

Gábor Erdős
  • 3,599
  • 4
  • 24
  • 56
-1

A list of lists (in your case l1 = [[1, 2, 3], [4, 5, 6]]) contains objects (list).

A slice of a list is a new shallow copy of that subset of the list where it inserts references to the objects. It can be seen from the following snippet code.

l1 = [[1, 2, 3], [4, 5, 6]]
l2 = l1[:]

print(id(l1))       # 4406414512
print(id(l2))       # 4382080248

# Shallow copy, insert references
print(id(l1[0]))    # 4439177768
print(id(l2[0]))    # 4439177768

To copy everything, use copy.deepcopy where it inserts copies of the objects. It can be seen from,

import copy

l1 = [[1, 2, 3], [4, 5, 6]]
l2 = copy.deepcopy(l1)

l2[0][0] = 100

print(l1)   # [[1, 2, 3], [4, 5, 6]]
print(l2)   # [[100, 2, 3], [4, 5, 6]]
SparkAndShine
  • 17,001
  • 22
  • 90
  • 134
-1

Your outer list my_list2 contains references to two other lists.

Even if you make an independent copy the outer list my_list2, it will still (initially) contain the same items, i.e. the same references to the same two lists. (If you're familiar with C/C++, you can loosely think of the references-to-lists as pointers.) That's called a "shallow copy". Here are a few ways of creating a shallow copy:

my_new_list2 = list(my_list2)
my_new_list2 = [item for item in my_list2]
my_new_list2 = my_list2[:]
import copy; my_new_list2 = copy.copy(my_list2)

To get the behaviour you want, you will need to duplicate the inner lists too. You can do that by hand, as others have suggested—or you can let the Python standard library do it automatically. That's what copy.deepcopy is for:

import copy
my_list_new2 = copy.deepcopy(my_list2)

The advantage of deepcopy is that it will work its way down an arbitrary number of levels of nesting, not necessarily just the first inner level.

jez
  • 14,867
  • 5
  • 37
  • 64