0

I have a question about how Python manage deep and shallow copies.

From what I have read:

  1. The shallow-copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
  2. The deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.

If you do:

a = list() 
b = a
a.append(1)
print(b)

It will show that b = [1]. If I change b it will modify a. If I change a it will modify b, as they are both pointing to the same list... (right?)

  • So, If I have understood it correctly, does this means that Python manage shallow copies with lists?? Is this b = a a shallow copy??

  • Do shallow/deep copies depend on the mutability (Mutable/Inmutable) of the classes?

Thank you in advance.

3 Answers3

2

TL;DR

  • Shallow copy makes a copy of the outer level items in the list, whatever nested inside those items will still be copied by reference
  • Deep copy goes through all nested items and copies each single one of them

Your example doesn't include copying, a = b will have both variables point to the same list.

Consider the following examples:

a = [[1, 2, 3], [4, 5, 6]]
b = copy.copy(a) # shallow copy, you can use b = list(a) too
a[0][0] = 5
# b[0][0] will be 5

a[0] = [0, 0]
# b[0] will not change

Why? Because shallow copy produces a copy of the elements in the list in the first level (b[0] is a copy of a[0]) but if a[0] is a list, anything inside that list is pointing to the same place as the elements in b[0].

While deep copy keeps going and copying all the elements no matter how nested the elements are

a = [[1, 2, 3], [4, 5, 6]]
b = copy.deepcopy(a) # deep copy
a[0][0] = 5
# b[0][0] will not change

a[0] = [0, 0]
# b[0] will not change
Mohd
  • 5,523
  • 7
  • 19
  • 30
  • You've made a completely false statement here, `a = b` does not do anything different based on whether an object is mutable or immutable. – Mark Ransom Jun 12 '20 at 21:16
  • @MarkRansom I didn't know that about immutables. Edited, thank you! – Mohd Jun 12 '20 at 23:28
1

From the docs:

  • A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
  • A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.

This interactive session might help you figure out the difference between shallow copy and deep copy:

In [91]: import copy

In [92]: z = [100, -1]

In [93]: a = [1, 2, z]

In [94]: b = a

In [95]: c = copy.copy(a)

In [96]: d = copy.deepcopy(a)

In [97]: a[0] = 99

In [98]: z[-1] = 0

In [99]: a
Out[99]: [99, 2, [100, 0]]

In [100]: b
Out[100]: [99, 2, [100, 0]]

In [101]: c
Out[101]: [1, 2, [100, 0]]

In [102]: d
Out[102]: [1, 2, [100, -1]]

Notice that the assignment b = a does not create a copy of the list referenced by a, it simply binds a new name b to that list.

A graphical representation of the objects in memory may provide you with further insight into these concepts.

screenshot from Python tutor online

Tonechas
  • 13,398
  • 16
  • 46
  • 80
0

Python never makes a copy with the assignment =, it just adds another way of referring to the same object. From the Zen of Python (PEP 20), "Explicit is better than implicit." If you want a copy you need to be explicit and ask for one.

a = list() 
b = a
a.append(1)
print(id(a), id(b))
1215215211272 1215215211272

import copy
c = copy.copy(a) # shallow copy
d = copy.deepcopy(a) # deep copy
print(id(a), id(c), id(a[0]), id(c[0]))
1215215211272 1215228911952 1215215269384 1215228911952
print(id(a), id(d), id(a[0]), id(d[0]))
1215215211272 1215228911952 1215215224136 1215228911952

The id will tell you if an object is the same or if it's a copy. In this case deepcopy didn't actually copy the integer, because it didn't need to - integers are immutable and you can't tell the difference between a copy and the original.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622