E0_copy is not a deep copy. You don't make a deep copy using list(). (Both list(...) and testList[:] are shallow copies, as well as testList.copy().)
You use copy.deepcopy(...) for deep copying a list.
copy.deepcopy(x[, memo])Return a deep copy of x.
See the following snippet -
>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = list(a)
>>> a
[[1, 2, 3], [4, 5, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]
>>> a[0][1] = 10
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b # b changes too -> Not a deepcopy.
[[1, 10, 3], [4, 5, 6]]
Now see the deepcopy operation
>>> import copy
>>> b = copy.deepcopy(a)
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b
[[1, 10, 3], [4, 5, 6]]
>>> a[0][1] = 9
>>> a
[[1, 9, 3], [4, 5, 6]]
>>> b # b doesn't change -> Deep Copy
[[1, 10, 3], [4, 5, 6]]
To explain, list(...) does not recursively make copies of the inner objects. It only makes a copy of the outermost list, while still referencing the same inner lists, hence, when you mutate the inner lists, the change is reflected in both the original list and the shallow copy. You can see that shallow copying references the inner lists by checking that id(a[0]) == id(b[0]) where b = list(a).
E0_copy is not a deep copy. You don't make a deep copy using list(). (Both list(...) and testList[:] are shallow copies, as well as testList.copy().)
You use copy.deepcopy(...) for deep copying a list.
copy.deepcopy(x[, memo])Return a deep copy of x.
See the following snippet -
>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = list(a)
>>> a
[[1, 2, 3], [4, 5, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]
>>> a[0][1] = 10
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b # b changes too -> Not a deepcopy.
[[1, 10, 3], [4, 5, 6]]
Now see the deepcopy operation
>>> import copy
>>> b = copy.deepcopy(a)
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b
[[1, 10, 3], [4, 5, 6]]
>>> a[0][1] = 9
>>> a
[[1, 9, 3], [4, 5, 6]]
>>> b # b doesn't change -> Deep Copy
[[1, 10, 3], [4, 5, 6]]
To explain, list(...) does not recursively make copies of the inner objects. It only makes a copy of the outermost list, while still referencing the same inner lists, hence, when you mutate the inner lists, the change is reflected in both the original list and the shallow copy. You can see that shallow copying references the inner lists by checking that id(a[0]) == id(b[0]) where b = list(a).
In Python, there is a module called copy with two useful functions:
import copy
copy.copy()
copy.deepcopy()
copy() is a shallow copy function. If the given argument is a compound data structure, for instance a list, then Python will create another object of the same type (in this case, a new list) but for everything inside the old list, only their reference is copied. Think of it like:
newList = [elem for elem in oldlist]
Intuitively, we could assume that deepcopy() would follow the same paradigm, and the only difference is that for each elem we will recursively call deepcopy, (just like mbguy's answer)
but this is wrong!
deepcopy() actually preserves the graphical structure of the original compound data:
a = [1,2]
b = [a,a] # there's only 1 object a
c = deepcopy(b)
# check the result
c[0] is a # False, a new object a_1 is created
c[0] is c[1] # True, c is [a_1, a_1] not [a_1, a_2]
This is the tricky part: during the process of deepcopy(), a hashtable (dictionary in Python) is used to map each old object ref onto each new object ref, which prevents unnecessary duplicates and thus preserves the structure of the copied compound data.
Official docs
deep copy of list in python - Stack Overflow
Why choose to copy a list with slices instead of copy(), deepcopy(), or list()??
copy vs deepcopy
Python copy.copy() appears to make a deep copy
I am fairly new to the Python realm. What is the difference between shallow and deep copy? I know the theoritical part of it but Im not actually able to understand while practicing it.
1)Does the list.copy() work on shallow copy technique? How does it work?
2)How does it differ from the other copying techniques like the = operator; slicing [:] ; list() constructor?
I have experimented it on the interactive python using two lists list1 and list2. When using "List2 = List1", I was able to see changes I made to the List2 on the List1. Whereas copying the contents of List1 to the List2 using " List2 = List1.copy()" , didn't do so.
Please enlighten me! Thanks in advance!
Your code does indeed succeed in creating a shallow copy. This can be seen by inspecting the IDs of the two outer lists, and noting that they differ.
>>> id(l)
140505607684808
>>> id(x)
140505607684680
Or simply comparing using is:
>>> x is l
False
However, because it is a shallow copy rather than a deep copy, the corresponding elements of the list are the same object as each other:
>>> x[0] is l[0]
True
This gives you the behaviour that you observed when the sub-lists are appended to.
If in fact what you wanted was a deep copy, then you could use copy.deepcopy. In this case the sublists are also new objects, and can be appended to without affecting the originals.
>>> from copy import deepcopy
>>> l=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> xdeep = deepcopy(l)
>>> xdeep == l
True
>>> xdeep is l
False <==== A shallow copy does the same here
>>> xdeep[0] is l[0]
False <==== But THIS is different from with a shallow copy
>>> xdeep[0].append(10)
>>> print(l)
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> print(xdeep)
[[1, 2, 3, 10], [4, 5, 6], [7, 8, 9]]
If you wanted to apply this in your function, you could do:
from copy import deepcopy
def processed(matrix,r,i):
new_matrix = deepcopy(matrix)
new_matrix[r].append(i)
return new_matrix
l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
x = processed(l,0,10)
print(x)
print(l)
If in fact you know that the matrix is always exactly 2 deep, then you could do it more efficiently than using deepcopy and without need for the import:
def processed(matrix,r,i):
new_matrix = [sublist[:] for sublist in matrix]
new_matrix[r].append(i)
return new_matrix
l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
x = processed(l,0,10)
print(x)
print(l)
What you're looking for is a deeper copy than what you did. A shallow copy only replaces the top layer, which does not seem to be what you're looking for. If you wanted a different outcome, try something like this:
def processed(matrix,r,i):
matrix[r] = [*matrix[r], i]
return matrix
l=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
x=l[:]
print(processed(x,0,10))
print(l)
The difference is that this makes two shallow copies - first to copy the outer list, and then the function copies the inner list before modifying it. The downside of this approach is that every call to processed now has extra overhead. If you wanted to do the copying all at once, you can do this:
def processed(matrix,r,i):
matrix[r].append(i)
return matrix
l=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
x=[inner[:] for inner in l]
print(processed(x,0,10))
print(l)
This copies two layers deep, using list comprehensions. Your structure only has two layers, so this fully copies the list.