Sitemap
Python in Plain English

New Python content every day. Follow to join our 3.5M+ monthly readers.

Mastering Python: Shallow vs. Deep Copy

Learn how shallow and deep copy work, their differences, and use cases.

11 min readOct 16, 2024

--

Press enter or click to view image in full size
Image created by me, CyCoderX

In Python, copying objects is a common task, especially when working with complex data structures such as lists, dictionaries, and classes. However, copying in Python can be tricky due to the distinction between shallow and deep copy operations. If you’re not careful, modifying a copied object might unintentionally change the original object, which can lead to unexpected behavior in your code.

Hi all, my name is CyCoderX and in this article, we’ll explore the difference between shallow and deep copy operations in Python, how they work, and when to use each. By understanding these concepts, you can avoid common pitfalls and write more robust, predictable Python code.

Let’s get started!

I write articles for everyone to enjoy, and I’d love your support by following me for more Python, SQL, Data Engineering and Data Science content.😊

What is Copying in Python?

Copying in Python refers to creating a new object that is a duplicate of an existing object. However, depending on how the copy is made, this new object can behave in different ways. Python provides two primary methods of copying:

  • Shallow Copy: Creates a new object but does not recursively copy the elements inside nested objects. Instead, it references the original objects within the copied object.
  • Deep Copy: Creates a new object and recursively copies all objects found within the original, meaning even nested objects are fully copied and no references are shared with the original object.

Before diving into shallow and deep copies, it’s important to understand that Python variables don’t store actual values but references to the objects. When you copy an object, you’re essentially creating another reference, and depending on whether it’s a shallow or deep copy, you may or may not duplicate the entire structure.

Shallow Copy in Python

A shallow copy creates a new object, but it only copies references to the elements inside the original object. In other words, it copies the structure of the object, but the content within it (such as other lists, dictionaries, or custom objects) is still shared between the original and the copied object. This means that any changes to the mutable elements inside the copied object will reflect in the original object, and vice versa.

How Shallow Copy Works

A shallow copy duplicates the top-level structure of the object, but the nested objects within it are not duplicated — they are merely referenced. This means that if the original object contains references to other mutable objects (like lists, dictionaries, or class instances), both the original and the shallow copy will point to the same inner objects.

Syntax for Shallow Copy:

  • Using copy() method: Many built-in types in Python, such as lists and dictionaries, have a copy() method that performs a shallow copy.
new_list = old_list.copy()
  • Using copy module: You can also use the copy module’s copy() function to explicitly perform a shallow copy.
import copy
new_object = copy.copy(old_object)

Usage and Example

Let’s look at a simple example to demonstrate shallow copy.

import copy

# Original list with a nested list
original_list = [[1, 2, 3], [4, 5, 6]]
shallow_copied_list = copy.copy(original_list)

# Modifying the shallow copy
shallow_copied_list[0][0] = 100

# Output
print("Original List:", original_list)
print("Shallow Copied List:", shallow_copied_list)

Output:

Original List: [[100, 2, 3], [4, 5, 6]]
Shallow Copied List: [[100, 2, 3], [4, 5, 6]]

In this example, both the original and the shallow copy reference the same nested list. Changing an element in the copied list affects the original list as well because they share the same inner objects.

Note: Only the outer structure (the list) is copied, but the inner lists are not.

Press enter or click to view image in full size
Photo by Steppeland - Lutgarde De Brouwer on Unsplash

Deep Copy in Python

A deep copy creates a new object and recursively copies all objects found within the original object. This means that a deep copy duplicates not only the outer structure of the object but also all of the nested objects, creating entirely new and independent objects. Unlike shallow copies, changes made to the copied object do not affect the original, even when dealing with nested objects.

How Deep Copy Works

In a deep copy, every object in the structure (including any nested objects) is fully duplicated, ensuring that no shared references exist between the original and the copy. This allows the copied object to be modified independently of the original, without any risk of unintentional side effects.

Syntax for Deep Copy:

  • Using copy module: To perform a deep copy, you can use the deepcopy() function from Python’s copy module.
import copy
deep_copied_object = copy.deepcopy(original_object)

Usage and Example

Let’s see an example of deep copy in action.

import copy

# Original list with a nested list
original_list = [[1, 2, 3], [4, 5, 6]]
deep_copied_list = copy.deepcopy(original_list)

# Modifying the deep copy
deep_copied_list[0][0] = 100

# Output
print("Original List:", original_list)
print("Deep Copied List:", deep_copied_list)

Output:

Original List: [[1, 2, 3], [4, 5, 6]]
Deep Copied List: [[100, 2, 3], [4, 5, 6]]

In this example, the deep copy creates an entirely separate copy of the nested list. Changes made to the deep copied list do not affect the original list, as each nested object is fully independent.

When to Use Deep Copy

Deep copy is useful when you’re working with complex data structures containing nested mutable objects and need a completely independent duplicate of the entire structure. This ensures that any modifications to the copied object do not affect the original.

Key Differences Between Shallow and Deep Copy

Understanding the key differences between shallow and deep copy is crucial when working with mutable objects in Python. While both operations create new objects, they differ significantly in how they handle nested objects and references.

1. Memory Allocation

  • Shallow Copy:

A shallow copy creates a new object, but it does not create new copies of the objects referenced within the original object. Instead, it reuses references to the original nested objects. As a result, shallow copies are more memory-efficient since they don’t duplicate all objects, but this can lead to unintended consequences if one of the shared references is modified.

  • Deep Copy:

A deep copy creates entirely new objects for both the top-level structure and all nested objects. Each object is a unique copy, with no shared references between the original and the copy. While this consumes more memory, it ensures complete independence between the original and the copied object.

2. Object References

  • Shallow Copy:

In a shallow copy, the outer object is duplicated, but the references to the nested objects are shared. This means that changes to the mutable elements within the shallow copy will also affect the original object because they reference the same nested objects.

Example:

import copy
original = [[1, 2], [3, 4]]
shallow_copy = copy.copy(original)
shallow_copy[0][0] = 99
print("Original:", original) # [[99, 2], [3, 4]] (affected)
  • Deep Copy:

In a deep copy, all objects (including nested ones) are copied recursively, meaning the references to the inner objects are not shared. Changes to the deep copy do not affect the original.

Get CyCoderX’s stories in your inbox

Join Medium for free to get updates from this writer.

Example:

import copy
original = [[1, 2], [3, 4]]
deep_copy = copy.deepcopy(original)
deep_copy[0][0] = 99
print("Original:", original) # [[1, 2], [3, 4]] (not affected)

3. Performance Considerations

  • Shallow Copy:

Shallow copies are faster and consume less memory since only the top-level object is copied. They are suitable when you don’t need to modify the nested objects independently or when working with simple data structures that don’t have nested mutable elements.

  • Deep Copy:

Deep copies can be slower and consume more memory because they recursively copy all objects. They are useful for complex data structures that contain nested mutable objects, where full independence between the original and copied objects is required.

4. Use Case Suitability

  • Shallow Copy:

Ideal when you only need to duplicate the outer structure and don’t plan to modify the nested objects. It’s also useful for performance when working with large, simple data structures.

  • Deep Copy:

Best suited for scenarios where you need a full, independent copy of an object and all of its nested elements. Deep copy is especially useful in situations where any modification to the copy should not affect the original.

Press enter or click to view image in full size
Photo by Arseny Togulev on Unsplash

When to Use Shallow Copy vs. Deep Copy

Choosing between shallow copy and deep copy depends on the complexity of your data structure and your specific use case. Below are guidelines to help you decide which copying method is appropriate for your project.

Use Shallow Copy When:

  • You Don’t Intend to Modify Nested Objects:

If you’re working with a data structure that contains nested objects but don’t plan to modify those nested objects, a shallow copy is sufficient. This is because the references to the inner objects will remain shared, and no conflicts will arise if you don’t modify them.

Example:

import copy
original = [[1, 2], [3, 4]]
shallow_copy = copy.copy(original)
# You can modify the top-level list, but not the inner lists.
shallow_copy.append([5, 6])
print("Original:", original) # [[1, 2], [3, 4]]
  • Memory Efficiency is a Priority:

If memory consumption is a concern and your data structure is large, shallow copy is preferable because it doesn’t duplicate nested objects, saving memory.

  • You’re Working with Immutable Nested Objects:

When the nested objects inside a data structure are immutable (such as tuples or strings), using a shallow copy is safe. Since immutable objects cannot be changed in place, the risk of unintended side effects from shared references is eliminated.

Example:

import copy
original = [("a", "b"), ("c", "d")]
shallow_copy = copy.copy(original)
# Since tuples are immutable, the shared reference isn't a concern.

Use Deep Copy When:

  • You Need Independent Copies of All Nested Objects:

If your data structure contains mutable objects (like lists or dictionaries) that you plan to modify independently, use deep copy. This ensures that changes to the copied object will not affect the original.

Example:

import copy
original = [[1, 2], [3, 4]]
deep_copy = copy.deepcopy(original)
deep_copy[0][0] = 99
print("Original:", original) # Original remains unchanged: [[1, 2], [3, 4]]
  • The Data Structure is Complex and Heavily Nested:

When working with deeply nested data structures, such as lists of lists, or dictionaries containing other dictionaries, deep copy is the safest option because it ensures complete duplication of every level of nesting.

  • You Want Complete Independence Between Copies:

Use deep copy when you need a fully independent copy of the entire data structure. This is useful when passing objects between functions or modules where you don’t want any risk of accidentally modifying the original object.

Practical Examples of Shallow and Deep Copy

Here are some practical examples to solidify your understanding of when and how to use shallow and deep copy in Python.

Example 1: Shallow Copy of a List with Nested Lists

import copy

# Original list with nested lists
original = [[1, 2], [3, 4]]
shallow_copy = copy.copy(original)

# Modifying the shallow copy
shallow_copy[0][0] = 99

print("Original List:", original) # Output: [[99, 2], [3, 4]]
print("Shallow Copied List:", shallow_copy) # Output: [[99, 2], [3, 4]]

In this case, modifying the shallow copy also modifies the original list, since both the original and the shallow copy share the same reference to the nested lists.

Example 2: Deep Copy of a List with Nested Lists

import copy

# Original list with nested lists
original = [[1, 2], [3, 4]]
deep_copy = copy.deepcopy(original)

# Modifying the deep copy
deep_copy[0][0] = 99

print("Original List:", original) # Output: [[1, 2], [3, 4]]
print("Deep Copied List:", deep_copy) # Output: [[99, 2], [3, 4]]

Here, modifying the deep copy does not affect the original list, as the nested lists have been fully duplicated, ensuring complete independence between the two objects.

Example 3: Shallow Copy with Immutable Objects

import copy

# Original list with immutable tuples
original = [(1, 2), (3, 4)]
shallow_copy = copy.copy(original)

# Modifying the shallow copy (modifying the top-level list, not the tuples)
shallow_copy.append((5, 6))

print("Original List:", original) # Output: [(1, 2), (3, 4)]
print("Shallow Copied List:", shallow_copy) # Output: [(1, 2), (3, 4), (5, 6)]

In this case, since the nested elements are immutable tuples, there’s no risk of accidentally modifying the original data. Shallow copying works perfectly here without side effects.

Conclusion

Understanding the differences between shallow and deep copy in Python is essential when working with mutable objects. While shallow copies are faster and more memory-efficient, they only duplicate the top-level structure, leaving inner references shared between the original and copied objects. Deep copies, on the other hand, create completely independent copies of all nested objects, ensuring that changes to one do not affect the other.

When deciding between shallow and deep copy, consider the complexity of your data structure and whether or not you need to modify nested objects independently. By using these copy operations correctly, you can avoid unintended side effects and write cleaner, more robust Python code.

Always remember to conduct your own research and verify the information you encounter. Relying solely on others can result in outdated or incorrect practices. Take the initiative to implement and refine the code you find, ensuring it meets your specific needs and standards.

Press enter or click to view image in full size
Photo by Markus Spiske on Unsplash

Final Words:

Thank you for taking the time to read my article.

This article was first published on Medium by CyCoderX.

Hey There! I’m CyCoderX, a data engineer who loves crafting end-to-end solutions. I write articles about Python, SQL, AI, Data Engineering, lifestyle and more!

If you want to explore similar articles and updates, feel free to explore my Medium profile:

Join me as we explore the exciting world of tech, data and beyond!

What did you think about this article? Let me know in the comments below … or above, depending on your device! 🙃

Please consider supporting me by:

  1. Clapping 50 times for this story
  2. Leaving a comment telling me your thoughts
  3. Highlighting your favorite part of the story

In Plain English 🚀

Thank you for being a part of the In Plain English community! Before you go:

--

--

CyCoderX
CyCoderX

Written by CyCoderX

Machine Learning & Data Engineer | Data Science | Python & SQL | AI | Software Developer | Azure & AWS Cloud Specialist | Blogger simplifying Big Data & Trends

Python in Plain English
Python in Plain English

Published in Python in Plain English

New Python content every day. Follow to join our 3.5M+ monthly readers.