The Secret Life of Python: The Copy Cat (Deep Copy)

Published: (February 24, 2026 at 10:26 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

Deepcopy vs. Slice: Which one actually protects your data?

🎧 Audio Edition: Prefer to listen? Check out the expanded AI podcast version of this deep dive on YouTube.
📺 Video Edition: Prefer to watch? Check out the 7‑minute visual explainer on YouTube.

Timothy was pale. He didn’t even look up when Margaret walked in with a fresh pot of Earl Grey.

“Margaret, I’ve seen a ghost,” Timothy whispered. “I was running a simulation for the Chess Club’s upcoming tournament. I made a Practice Bracket so I could test some player movements without touching the Official Bracket. But… when I changed the Practice version, the Official one changed itself.”

He showed her his code:

# The Official Bracket: A list of teams (nested lists)
official_bracket = [["Alex", "Alice"], ["Bob", "Barbara"]]

# Timothy makes a "Practice" copy using a slice
practice_bracket = official_bracket[:]

# He swaps a player in the first match of the practice bracket
practice_bracket[0][0] = "Timothy"

print(f"Practice: {practice_bracket}")
print(f"Official: {official_bracket}")

Output

Practice: [['Timothy', 'Alice'], ['Bob', 'Barbara']]
Official: [['Timothy', 'Alice'], ['Bob', 'Barbara']]

“See?” Timothy pointed at the screen. “I never touched official_bracket[0][0]. I only touched the practice copy. But the change followed me. It’s a ghost in the machine.”

The Photocopy of Addresses

Margaret pulled up a chair.

“It’s not a ghost, Timothy. It’s a shallow copy. You thought you were photocopying the documents, but you were actually just photocopying a list of addresses.”

When you do official_bracket[:], Python creates a new outer list (a new envelope), but the inner lists (the matches) are still the same objects. Changing an inner element through one list mutates the same object seen by the other list.

The Shallow Limit

“I thought the slice [:] was the standard way to copy a list? We used it to fix the skipping loop bug back in Episode 19!”

It works perfectly for flat lists (e.g., a list of strings or numbers). As soon as a list contains other mutable objects, the slice only copies the top layer, leaving inner references shared.

The Deep Solution

To obtain a completely independent copy, use copy.deepcopy:

import copy

official_bracket = [["Alex", "Alice"], ["Bob", "Barbara"]]

# The Deep Solution
practice_bracket = copy.deepcopy(official_bracket)

# Now swap the player
practice_bracket[0][0] = "Timothy"

print(f"Official: {official_bracket[0][0]}")  # Alex
print(f"Practice: {practice_bracket[0][0]}")  # Timothy

deepcopy traverses every nested object, creating new copies along the way. It is thorough but more expensive in terms of time and memory, so reserve it for nested data structures.

Margaret’s Cheat Sheet: The Copy Cat

OperationWhat it doesSafe for
Assignment (b = a)No copy; both names reference the same object
Shallow Copy (b = a[:] or b = a.copy())Copies only the outer containerFlat lists (strings, numbers)
Deep Copy (b = copy.deepcopy(a))Recursively copies all nested objectsAny nested structure (lists, dicts, custom objects)

Pro tip: To verify whether inner objects are shared, compare their identities:

id(official_bracket[0]) == id(practice_bracket[0])  # True → still shared

If the result is True, you’re still dealing with a shallow copy.

In the next episode, Margaret and Timothy will face “The Default Trap”—where Timothy learns that giving a function a default list is like sharing a toothbrush with a stranger.

0 views
Back to Blog

Related posts

Read more »

[Boost]

Profile !Vincent A. Cicirellohttps://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaw...