Shallow Copy and Deep Copy in Python

Shallow Copy and Deep Copy in Python

In a previous article, we delved into the topic of memory references in Python. It is highly recommended that you revisit that article before proceeding with this one, as a solid grasp of memory references will greatly enhance your understanding of the concepts we're about to explore.

Shallow Copy

To illustrate the concept of a shallow copy, let's examine the following example:

lst = [1, [1, 2], 3]

copied = lst.copy()

copied[1].append('a')

print(lst)     # Output: [1, [1, 2, 'a'], 3]
print(copied)  # Output: [1, [1, 2, 'a'], 3]

In the above example, we've defined a list, lst, which contains an integer, an inner list, and another integer.

Next, we use the copy() method of the list to create a copy of it, storing it in a new variable called copied.

We proceed to add a new element, 'a', to the inner list within the copied variable.

Surprisingly, when we print both lists, we notice that the change made to the inner list of copied has also been reflected in lst.

Why did this happen? The reason is relatively straightforward. When we copied a list into another list, it actually generated a shallow copy of the list. In our original list, for the inner list, instead of duplicating the values, Python copied a reference to the list itself. Consequently, when it was copied into another list, the same reference was essentially duplicated. As a result, any changes made to the inner list in one variable were also manifested in the other, since both variables were referencing the same memory address.

Understanding this concept of shallow copy is paramount when dealing with intricate data structures in Python, as it can lead to unexpected outcomes if not considered carefully.

Deep Copy

To address the issue described above and create a deeper copy, where not only the top-level object but all nested objects are duplicated independently, you can use the deepcopy method from the copy module:

import copy

lst = [1, [1, 2], 3]

copied = copy.deepcopy(lst)

copied[1].append('a')

print(lst)     # Output: [1, [1, 2], 3]
print(copied)  # Output: [1, [1, 2, 'a'], 3]

In this example, instead of using the copy method of the list, we employ the deepcopy function from the copy module. Now, we obtain the expected output: changes to the copied object does not affect the original lst.

Deep copying is particularly valuable when dealing with intricate data structures or objects that possess nested structures. It ensures that changes made to one copy do not inadvertently impact other copies by ensuring complete independence of nested objects.