Here's a brief review of Shallow Copy:
L1 = [[1, 2], [3, 4]]
L2 = L1[:]
Here, L1[0]
and L2[0]
are aliases. Changing the contents of L1[0]
(e.g. with L1[0][0] = 5
) will also change the contents of L2[0]
and vice versa. To address this, we can instead create a deep copy of L1
:
copy = []
for e in L1:
copy.append(e[:])
Here, we are creating a new list every time we append, since we are appending a copy of e
, not e
itself. However, this only works if L1
happens to be a list of lists of int
s. For example, this would create a problem:
L1 = [[1, 2], [3, [4, 5]]]
copy = []
for e in L1:
copy.append(e[:])
We are not creating a copy of [4, 5]
here, only a copy of [3, [4, 5]
. That means that L1[1][1]
and copy[1][1]
will be alises. Changing one will change the other.
If, instead of appending the shallow copy of e
(i.e., e[:]
), we were to append the deep copy of e
, things would work. We use a recursive solution to accomplish this.
(Side note: type(obj)
returns the type of obj
. For example, type([1, 2, 3]) == list
is True
.
def deep_copy(obj):
'''Return a deep (i.e., non-aliased at all) copy of obj, a nested list of
integers'''
#Base case:
if type(obj) != list:
return obj
copy = []
for elem in obj:
#The leap of faith: assume that deep_copy works!
copy.append(deep_copy(elem))
return copy
The base case is easy: if obj
is not a list, then it's an integer, and we consider int
s to be their own deep copies. Otherwise, we perform the recursive leap of faith: we append to copy
the deep copies of each of obj
's elements. If we assume deep_copy
works, then the idea works as a whole.