Introduction
In this article, we'll take a look at how to deep and shallow copy the objects in Python.
The short answer is that you can use methods of the copy
module, for both operations:
import copy
shallow_copy_list = copy.copy(original_list)
deepcopy_list = copy.deepcopy(original_list)
Though, what does it mean to copy something in a shallow or deep fashion?
In the proceeding sections, we'll dive into what these terms mean, how Python treats object references and objects in memory, and why these two methods work the way they do.
Shallow Copy an Object in Python
When we use assignment statements (=
) in Python to create copies of compound objects, such as lists or class instances or basically any objects that contain some other objects, Python does not clone the object itself. Instead, it simply binds the reference to the targeted object.
Imagine that we have a list with the following elements in it:
original_list =[[1,2,3], [4,5,6], ["X", "Y", "Z"]]
If we try to copy our original list using the assignment statement as follows:
shallow_copy_list = original_list
print(shallow_copy_list)
It may look like that we cloned our object and now have two of them:
[[1,2,3], [4,5,6], ['X', 'Y', 'Z']]
But, do we really have two objects? No, we don't. We have two reference variables pointing to the same object in memory. This can easily be verified by printing the ID of the object in memory for both of these:
id(original_list) # 4517445712
id(shallow_copy_list) # 4517445712
A more tangible proof of this can be observed by attempting to change a value in either of "the two lists" - while in reality, we change the same list, and both pointers point to the same object in memory.
Let's access the last element of the object pointed to by original_list
:
# Last element of last element
original_list[-1][-1] = "ZZZ"
print(original_list)
This results in:
[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'ZZZ']]
Knowing that both reference variables point to the same object, printing shallow_copy_list
will return the same result:
print(shallow_copy_list)
[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'ZZZ']]
Shallow Copying is the process of copying a reference to an object and storing it in a new variable. The
original_list
andshallow_copy_list
are merely references that point to the same addresses in memory (RAM), that store the values of[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'ZZZ']]
.
It is also possible to create a shallow copy of an object using a slice of the entire list and assignment statement:
slice_shallow_copy_list = original_list[:]
Another way to shallow copy is to use the copy
module of Python’s standard library.
To use the copy
module, we must first import it:
import copy
Now we can use the copy()
method of the copy
module:
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
second_shallow_copy_list = copy.copy(original_list)
Print them both to see that if they reference the same values:
print(original_list)
print(second_shallow_copy_list)
As expected, they do:
[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'ZZZ']]
[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'ZZZ']]
Usually, you would want to copy a compound object, for example at the beginning of a method, then modify the clone, but keep the original object as it was in order to use it again later sometime.
To achieve this, we need to deep copy the object. Now let's learn what is a deep copy and how to deep copy a compound object.
Deep Copy an Object in Python
Deep copying an object means really cloning the object and its values into a new copy (instance) in memory, with those same values.
Rather than creating a new reference to the same values, with the deep copy, we can actually create a new object that is independent of the original data but contains the same values.
In a typical deep copy process, first, a new object reference is created, then all the child objects are added to the parent object recursively.
This way, unlike a shallow copy, making any modification to the original object, does not reflect in the copy object (or vice versa).
Here is a simple illustration of a typical deep copy:
To deep copy an object in Python, we use the deepcopy()
method of the copy
module.
Let's import the copy module and create a deep copy of a list:
import copy
original_list = [[1,2,3], [4,5,6], ["X", "Y", "Z"]]
deepcopy_list = copy.deepcopy(original_list)
Now let's print our lists to make sure the outputs are the same, as well as their IDs as proof of their uniqueness:
print(id(original_list), original_list)
print(id(deepcopy_list), deepcopy_list)
The output confirms that we've created ourselves a genuine copy:
4517599280, [[1, 2, 3], [4, 5, 6], ['X', 'Y', 'Z']]
4517599424, [[1, 2, 3], [4, 5, 6], ['X', 'Y', 'Z']]
Now let's try to modify our original list by changing the last element of the last list to the "O", and then print it to see the result:
original_list[-1][-1] = "O"
print(original_list)
We get the results as expected:
[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'O']]
Now if we go ahead and try to print our copy list:
print(deepcopy_list)
The previous modification did not reflect on this list:
[[1, 2, 3], [4, 5, 6], ['X', 'Y', 'Z']]
Remember that the copy()
and deepcopy()
methods are applicable on other compound objects. This means that you can use them to create copies of class instances as well.
Conclusion
In this article, we learned what it means to shallow copy and to deep copy an object.
We also learned that we can use the copy()
method of the copy
module to create a shallow copy, and the deepcopy()
method to create a deep copy of the compound objects.