Fundamentals 10 min read

Master Python Shallow vs Deep Copy: Avoid Hidden Bugs and Boost Performance

This article explains the difference between shallow and deep copying in Python, shows how mutable and immutable objects behave, provides practical code examples, performance benchmarks, and best‑practice guidelines to help developers choose the right copying method for their data structures.

Python Crawling & Data Mining
Python Crawling & Data Mining
Python Crawling & Data Mining
Master Python Shallow vs Deep Copy: Avoid Hidden Bugs and Boost Performance

1. A Confusing Example

original_list = [1, 2, [3, 4]]
copied_list = original_list.copy()  # shallow copy
copied_list[0] = 99
copied_list[2][0] = 88
print("Original list:", original_list)
print("Copied list:", copied_list)

Output:

Original list: [1, 2, [88, 4]]
Copied list: [99, 2, [88, 4]]

Modifying the nested element of copied_list also changes original_list because list.copy() performs a shallow copy.

2. Visualizing Memory Structure

Memory diagram for shallow copy
Memory diagram for shallow copy

Understanding how Python stores objects helps explain why shallow copies share inner objects while deep copies duplicate everything.

Immutable Objects

Numbers (int, float)

Strings (str)

Tuples (tuple)

Booleans (bool)

These objects cannot be modified; any change creates a new object.

Mutable Objects

Lists (list)

Dictionaries (dict)

Sets (set)

Custom objects

These can be changed in place, which is why copying behavior matters.

3. Shallow Copy (Shallow Copy): Surface Level

Common ways to create a shallow copy:

import copy
new_list = copy.copy(original_list)   # method 1
new_list = original_list.copy()       # method 2
new_list = original_list[:]           # method 3
new_list = list(original_list)       # method 4
Shallow copy memory diagram
Shallow copy memory diagram

Example verification:

original = [1, 2, [3, 4]]
shallow_copied = original.copy()
shallow_copied[0] = 99
print("After modifying first level:", shallow_copied)
print("Original:", original)
shallow_copied[2][0] = 88
print("After modifying second level:", shallow_copied)
print("Original:", original)

4. Deep Copy (Deep Copy): Complete Separation

import copy
original = [1, 2, [3, 4]]
deep_copied = copy.deepcopy(original)
Deep copy memory diagram
Deep copy memory diagram

Example verification:

original = [1, 2, [3, 4]]
deep_copied = copy.deepcopy(original)
deep_copied[0] = 99
deep_copied[2][0] = 88
print("Original:", original)
print("Deep copy:", deep_copied)

5. Practical Scenarios: When to Use Which Copy

Scenario 1 – Shallow copy is sufficient

simple_list = [1, 2, 3, 4, 5]
shallow_copy = simple_list.copy()
config = {'debug': True, 'level': 'info'}
config_copy = config.copy()

Scenario 2 – Need a deep copy

import copy
nested_data = {'name': 'test', 'settings': {'color': 'blue', 'size': [10, 20, 30]}}
independent_copy = copy.deepcopy(nested_data)
independent_copy['settings']['size'][0] = 100
print(nested_data['settings']['size'][0])  # still 10

Scenario 3 – Custom objects

class Node:
    def __init__(self, value, children=None):
        self.value = value
        self.children = children if children else []

node1 = Node(1, [Node(2), Node(3)])
shallow_node = copy.copy(node1)
shallow_node.children[0].value = 999  # affects original

deep_node = copy.deepcopy(node1)
deep_node.children[0].value = 888  # original unchanged

6. Performance Considerations

import copy, time
big_data = [[i for i in range(1000)] for _ in range(1000)]
start = time.time()
shallow_copy = copy.copy(big_data)
print(f"Shallow copy time: {time.time() - start:.4f}s")
start = time.time()
deep_copy = copy.deepcopy(big_data)
print(f"Deep copy time: {time.time() - start:.4f}s")

7. Best Practices

Understand object structure : analyze nesting before copying.

Choose wisely : use shallow copy for simple structures, deep copy for complex nested data.

Be performance‑aware : avoid deep copy on large data unless necessary.

Know the requirement : sometimes shared references are intentional.

Test : write small snippets to verify copy behavior.

8. Decision Flow

Need completely independent copy → use deepcopy Only first level independent → use copy or .copy() Shared reference is fine → assign directly

9. Interactive Question

What copy‑related pitfalls have you encountered in your projects? Share your experiences in the comments!

10. Next Article Preview

Upcoming: “Concurrency Choices in Python – Threads, Processes, or Coroutines?” – we’ll help you navigate the trade‑offs.

PerformancePythondeep copyshallow copy
Python Crawling & Data Mining
Written by

Python Crawling & Data Mining

Life's short, I code in Python. This channel shares Python web crawling, data mining, analysis, processing, visualization, automated testing, DevOps, big data, AI, cloud computing, machine learning tools, resources, news, technical articles, tutorial videos and learning materials. Join us!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.