Shallow copy and deep copy in Python

While programming, We need to copy existing data. When we assign a variable to another using = operator, the assignment operator doesn’t  copy the object and just creates a new reference to the same object.In this article, we will study how to copy an object using shallow copy and deep copy in python. We will also implement programs to understand their work.

How to use shallow copy in python?

Shallow copy is a function used to create a copy of an existing collection object. When we try to copy a collection object using shallow copy, It creates a new collection object and stores references to the elements of the original object.

We can use shallow copy in python using the copy module. To perform a shallow copy operation, we use the copy() method of the copy() module.

The copy() method takes the original  collection object as input and creates a new collection object with reference to the elements of the original collection object.It then returns a reference to the new collection object.

In the following program, we will create a copy of the given python dictionary using the copy() method.

import copy
myDict={1:3,4:9,6:12,5:11}
print("Original Dictionary is:")
print(myDict)
newDict=copy.copy(myDict)
print("New Dictionary is:")
print(newDict)

Output:

Original Dictionary is:
{1: 3, 4: 9, 6: 12, 5: 11}
New Dictionary is:
{1: 3, 4: 9, 6: 12, 5: 11}

In the output, we can see that we have created a similar dictionary as the original dictionary given in the program.

How does shallow copy work?

In shallow copy, when the new object is created, it has references to the elements of the original object. If we try to make any changes to the newly created object, it will not get reflected in the original object given that the elements in the objects should not refer to another object i.e there should not be any nesting.

import copy
myDict={1:3,4:9,6:12,5:11}
print("Original Dictionary is:")
print(myDict)
newDict=copy.copy(myDict)
print("New Dictionary is:")
print(newDict)
newDict[1]=5
print("Original Dictionary after change is:")
print(myDict)
print("New Dictionary after change is:")
print(newDict)

Output:

Original Dictionary is:
{1: 3, 4: 9, 6: 12, 5: 11}
New Dictionary is:
{1: 3, 4: 9, 6: 12, 5: 11}
Original Dictionary after change is:
{1: 3, 4: 9, 6: 12, 5: 11}
New Dictionary after change is:
{1: 5, 4: 9, 6: 12, 5: 11}

If we will make any changes to the original object, it also doesn’t reflected in the copy of the original object given that nesting should not be done.

import copy
myDict={1:3,4:9,6:12,5:11}
print("Original Dictionary is:")
print(myDict)
newDict=copy.copy(myDict)
print("New Dictionary is:")
print(newDict)
myDict[1]=5
print("Original Dictionary after change is:")
print(myDict)
print("New Dictionary after change is:")
print(newDict)

Output:

Original Dictionary is:
{1: 3, 4: 9, 6: 12, 5: 11}
New Dictionary is:
{1: 3, 4: 9, 6: 12, 5: 11}
Original Dictionary after change is:
{1: 5, 4: 9, 6: 12, 5: 11}
New Dictionary after change is:
{1: 3, 4: 9, 6: 12, 5: 11}

Similarly, when we add any element to the original object, it will not have any impact on the new object.

import copy
myDict={1:3,4:9,6:12,5:11}
print("Original Dictionary is:")
print(myDict)
newDict=copy.copy(myDict)
print("New Dictionary is:")
print(newDict)
myDict[7]=49
print("Original Dictionary after change is:")
print(myDict)
print("New Dictionary after change is:")
print(newDict)

Output:

Original Dictionary is:
{1: 3, 4: 9, 6: 12, 5: 11}
New Dictionary is:
{1: 3, 4: 9, 6: 12, 5: 11}
Original Dictionary after change is:
{1: 3, 4: 9, 6: 12, 5: 11, 7: 49}
New Dictionary after change is:
{1: 3, 4: 9, 6: 12, 5: 11}

The scenarios discussed above change when there is nesting present in the objects. I.e. When the objects being copied contain other objects, changes which occur on nested objects are visible in both the original as well as copied object. This can be seen as follows.

import copy
myDict={1:3,4:9,6:12,5:{10:11}}
print("Original Dictionary is:")
print(myDict)
newDict=copy.copy(myDict)
print("New Dictionary is:")
print(newDict)
myDict[5][10]=49
print("Original Dictionary after change is:")
print(myDict)
print("New Dictionary after change is:")
print(newDict)

Output:

Original Dictionary is:
{1: 3, 4: 9, 6: 12, 5: {10: 11}}
New Dictionary is:
{1: 3, 4: 9, 6: 12, 5: {10: 11}}
Original Dictionary after change is:
{1: 3, 4: 9, 6: 12, 5: {10: 49}}
New Dictionary after change is:
{1: 3, 4: 9, 6: 12, 5: {10: 49}}

This happens because when we copy an object using copy.copy() method, only the copy of the object being passed as parameter to the copy() method is created. The elements inside the object are not copied and only references to the elements are copied. So, when only primitive data types like int, double, string are present as elements in the original object, changes are not visible to the new object when done in the original object because these data types are immutable and for every change, a new object is created. But in case of nested objects, references are not changed and when we make any change to one of the objects, it is visible in another object.

How to use deep copy in python?

To avoid the problem discussed while doing shallow copy, we will use the deepcopy() method. The deepcopy() method creates a copy of every element in the object recursively and doesn’t copy references.This can be done as follows.

import copy
myDict={1:3,4:9,6:12,5:{10:11}}
print("Original Dictionary is:")
print(myDict)
newDict=copy.deepcopy(myDict)
print("New Dictionary is:")
print(newDict)

Output:

Original Dictionary is:
{1: 3, 4: 9, 6: 12, 5: {10: 11}}
New Dictionary is:
{1: 3, 4: 9, 6: 12, 5: {10: 11}}

After using deepcopy(), changes made in the original object will not be shown in the copied object even if nesting is present. This can be seen as follows.

import copy
myDict={1:3,4:9,6:12,5:{10:11}}
print("Original Dictionary is:")
print(myDict)
newDict=copy.deepcopy(myDict)
print("New Dictionary is:")
print(newDict)
myDict[5][10]=49
print("Original Dictionary after change is:")
print(myDict)
print("New Dictionary after change is:")
print(newDict)
Output:
Original Dictionary is:
{1: 3, 4: 9, 6: 12, 5: {10: 11}}
New Dictionary is:
{1: 3, 4: 9, 6: 12, 5: {10: 11}}
Original Dictionary after change is:
{1: 3, 4: 9, 6: 12, 5: {10: 49}}
New Dictionary after change is:
{1: 3, 4: 9, 6: 12, 5: {10: 11}}

Here we can see that unlike copy(), when we copy an object using deepcopy(), the changes made in original object doesn’t affect the copied object and vice versa because the object created by deepcopy() method doesn’t contain any reference to elements of original dictionary while in case of copy() method, the newly created objects contain references to elements of the original object.

Conclusion

In this article, we have discussed shallow copy and deep copy in python. We have seen that when nested objects are present then deepcopy() should be used to create a copy of the objects. We can also write the programs used in this article with exception handling using python try except to make the programs more robust and handle errors in a systematic way.

Leave a Reply

Your email address will not be published. Required fields are marked *