Guide Python Intermediate

6.5 Copying Objects

The difference between assignment, a shallow copy, and a deep copy in Python, when each is appropriate, and how the copy module's copy() and deepcopy() behave differently on nested mutable objects.

3 min read

When shared references aren’t what you want (see 6.2 Objects and Variable References), Python offers three distinct ways to copy a mutable object, each with a different depth.

flowchart TD subgraph Shallow["Shallow Copy — copy.copy(original)"] O1["original"] --> L1["[1, 2, ...] (new outer list)"] S1["shallow"] --> L1 L1 --> N1["[3, 4] (SHARED nested list)"] end subgraph Deep["Deep Copy — copy.deepcopy(original)"] O2["original"] --> L2["[1, 2, ...]"] D2["deep"] --> L3["[1, 2, ...] (own copy)"] L2 --> N2["[3, 4] (original)"] L3 --> N3["[3, 4] (own copy)"] end

Shallow copy duplicates the outer container but shares nested objects; deep copy duplicates everything, recursively.

Assignment Copy

b = a is not a copy at all — both names reference the identical object. Included here only to contrast with the two real copy types below.

Shallow Copy

What Is It?

Creates a new outer object, but the elements inside it are still shared references to the same nested objects as the original.

Why Is It Used?

Fast, and sufficient when the container holds only immutable elements, or nested mutation isn’t a concern.

Deep Copy

What Is It?

Recursively copies the object and everything it contains, so the result is completely independent of the original at every level.

Why Is It Used?

Needed whenever the original contains nested mutable objects (like a list of lists) and full independence is required.

The copy Module

The standard library’s copy module provides both copy() and deepcopy() — here’s all three approaches compared directly:

>>> import copy
>>> original = [1, 2, [3, 4]]
>>> assign_copy = original
>>> shallow = copy.copy(original)
>>> deep = copy.deepcopy(original)

>>> original[2].append(99)      # mutate the nested list

>>> original
[1, 2, [3, 4, 99]]
>>> assign_copy                 # same object -- sees the change
[1, 2, [3, 4, 99]]
>>> shallow                     # outer copied, but nested list is SHARED -- sees it
[1, 2, [3, 4, 99]]
>>> deep                        # fully independent -- does NOT see the change
[1, 2, [3, 4]]

Quick Interview Answer

b = a isn’t a copy at all — both names share one object. copy.copy() creates a shallow copy: a new outer container, but its nested elements are still shared with the original, so mutating a nested list through either one is visible through both. copy.deepcopy() recursively copies everything, producing a result that’s fully independent at every level. Use shallow copy when the container only holds immutable elements; use deep copy whenever it holds nested mutable objects and true independence is required.”

Common Mistakes

  • Assuming copy.copy() produces a fully independent object — it only copies the outer container; nested mutable objects are still shared.
  • Reaching for copy.deepcopy() by default even when not needed — it’s slower and unnecessary for flat structures or containers of immutable values.
  • Using list(original) or original[:] expecting a deep copy — both are shallow copies, identical in depth to copy.copy().

Add More Questions to This Guide

Know a question that should be here? Share it and help the community!

Open Google Form