What's the Difference Between Shallow and Deep Copies in Python? | Towards Data Science

Python comes with a module called copy that offers certain copy functionality. In this article, we are going to explore what deep and shallow copies are. Additionally, we’ll discuss about their differences and when to use one over the other.
Before jumping into shallow and deep copies in Python, it is important to first understand the difference between mutable and immutable object types. As the name suggests, immutable objects can’e be altered and therefore, Python will instead create a new object when the value of such object is modified.
For instance, let’s assume that we have two variables referencing the same integer object:
>>> a = 10>>> b = a  # variables a and b hold the reference to the same object
Now if we perform any sort of operation on variable a— and given that integers in Python are immutable — the result will essentially be a new object that holds the new value. This means that the old value of the object (and thus all variables referencing it), will remain unchanged:
>>> a = a + 1>>> print(a)11>>> print(b)10
On the other hand, mutable object types allow in-place modifications of the object value. This means that when the value of a mutable object type is modified, there is an impact on all variables holding the reference to the same object. For instance, let’s assume we do have the following lists
>>> list_1 = [1, 2, 3]>>> list_2 = list_1
Given that lists in Python are mutable, if we change any of the two lists this action will have a direct impact on the other variable too, as they both point to the same object reference in memory.
>>> list_1[0] = 0>>> print(list_1)[0, 2, 3]>>> print(list_2)[0, 2, 3]
For more details regarding the Dynamic Typing model of Python you can read one of my articles here on Medium:
towardsdatascience.com
The most straight-forward approach to copy objects is through normal assignment operations. Let’s assume that we have the following operations
a = [1, 2, 3]b = a
In this case, both variables a and b hold the same reference to the same object. This means that if any of the two variables are used to perform an in-place modification, the other variable will be impacted too.
>>> a[0] = 0>>> print(a)[0, 2, 3]>>> print(b)[0, 2, 3]
Therefore normal assignment operations are typically used when we have to deal with immutable object types. In this case, when an operation is performed using any of the two variables, the other variable will remain unchanged since its reference is pointing towards the old object that remains unchanged too, given that it is immutable.
>>> id(a) == id(b)True
Assignment statements in Python do not copy objects, they create bindings between a target and an object — Python Docs
Before jumping into the details of shallow and deep copies, note that their difference is only relevant when we have to deal with compound objects which essentially are nested structures. In other words, compound objects are objects that contain other objects — for example, a list of lists or a dictionary of sets.
A shallow copy will take a copy of the original object and create a new compound object but if the object we are copying is a compound object the inner objects will be the same as the ones found in the original object.
>>> import copy>>> b = copy.copy(a)>>> id(a) == id(b)False
As we can see, list objects a and b are different which means they hold different references that point to different objects in memory (even though the values of these objects are the same).
Things get a little bit more complicated when we need to deal with compound objects. Now let’s assume that variable a is a compound object that represents a list of lists:
a = [[1, 2, 3], [4, 5, 6]]
Now we take a shallow copy of a
>>> import copy>>> b = copy.copy(a)
and we can see that a and b are different objects
>>> id(a) == id(b)False
however, the inner objects (i.e. the two inner lists) are the same as the ones referenced by the original object:
>>> id(a[0]) == id(b[0])True
This is quite dangerous as a change in any of the inner lists will impact the other compound object referencing these inner lists:
>>> a[0][0] = 0>>> a[[0, 2, 3], [4, 5, 6]]>>> b[[0, 2, 3], [4, 5, 6]]
Therefore, a shallow copy is suitable only when we don’t have to deal with compound objects.
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 — Python Docs
A deep copy will take a copy of the original object and will then recursively take copy of the inner objects which are found (if any).
>>> import copy>>> a = [[1, 2, 3], [4, 5, 6]]>>> b = copy.deepcopy(a)
Again, we can see that the original and copied objects are essentially different:
>>> id(a) == id(b)False
but in this case, even the inner objects will be different:
>>> id(a[0]) == id(b[0])False
This means that a change in any of the nested lists in a wont affect the corresponding lists in object b :
>>> a[0][0] = 0>>> a[[0, 2, 3], [4, 5, 6]]>>> b[[1, 2, 3], [4, 5, 6]]
Therefore, a deep copy is more suitable when we have to deal with compound objects and want to ensure that a change in any of the inner objects won’t affect other variables referencing the same objects.
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original — Python Docs
In this article, we explored the three basic ways one can use to copy objects in Python. Initially, we discussed about the difference between immutable and mutable object types. It is not necessary for immutable object types to be copied as the values of such instances will never change. On the other hand, developers need to be careful when modifying mutable object types as this action can potentially impact other variables holding the reference to the same object. When such object is changed in-place, then all other variables referencing the same object will be affected by this change as well.
Therefore, it is important to understand how to properly copy mutable objects in order to avoid bugs in your code. Recall that a shallow copy will create a new object out of the original however if the object contains other objects then the inner objects won’t be copied but instead, the same reference will be used as the ones of the original structure. On the other hand, a deep copy will create a new object even for the inner objects contained in a compound object.