Last modified: Feb 11, 2026 By Alexander Williams
Python Copy Object Guide: Shallow vs Deep Copy
Copying objects in Python is a common task. It seems simple. But it can lead to bugs. Understanding the difference between copying a reference and copying the data is crucial.
This guide explains the three main ways to copy objects. We will cover assignment, shallow copy, and deep copy. You will learn when to use each method.
Why Copying Objects Matters
In Python, variables are names that refer to objects. When you assign one variable to another, you are often just creating a new reference to the same object.
This is efficient. But it means changes to one variable affect the other. This is not always what you want. Creating a true independent copy prevents this.
This is especially important with mutable objects like lists and dictionaries. For a deeper dive into object fundamentals, see our Python Objects: Classes, Instances, and Methods Guide.
Method 1: Assignment (Creating a Reference)
The equal sign (=) does not create a copy. It creates a new name (reference) pointing to the same object in memory.
This is the default behavior. It is fast and memory-efficient. But it leads to shared state.
# Assignment creates a reference, not a copy
original_list = [1, 2, 3]
new_reference = original_list # Both names point to the same list object
# Modify through the new reference
new_reference.append(4)
print("Original List:", original_list)
print("New Reference:", new_reference)
print("Are they the same object?", original_list is new_reference)
Original List: [1, 2, 3, 4]
New Reference: [1, 2, 3, 4]
Are they the same object? True
Both variables show the change. They are the same object. This is often a source of unexpected bugs.
Method 2: Shallow Copy
A shallow copy creates a new outer object. But it populates it with references to the same inner objects found in the original.
You can create a shallow copy using the copy() method (for lists, dicts) or the copy.copy() function from the `copy` module.
import copy
# Original list containing mutable objects (inner lists)
original = [1, 2, [3, 4]]
shallow_copied = copy.copy(original) # Or original.copy() for lists
# Modify the top-level element (an integer)
shallow_copied[0] = 99
print("After modifying top-level int:")
print("Original:", original)
print("Shallow Copy:", shallow_copied)
# Modify the nested mutable object (the inner list)
shallow_copied[2].append(5)
print("\nAfter modifying nested list:")
print("Original:", original) # The inner list is changed here too!
print("Shallow Copy:", shallow_copied)
After modifying top-level int:
Original: [1, 2, [3, 4]]
Shallow Copy: [99, 2, [3, 4]]
After modifying nested list:
Original: [1, 2, [3, 4, 5]]
Shallow Copy: [99, 2, [3, 4, 5]]
The top-level change (99) is independent. The change to the nested list is shared. The inner list object was not copied, only referenced.
For more on nested structures, read Python Objects in Objects: Nested Data Guide.
Method 3: Deep Copy
A deep copy creates a new outer object. It then recursively creates copies of all objects found within the original.
This results in a fully independent clone. Use the copy.deepcopy() function from the `copy` module.
import copy
original = [1, 2, [3, 4]]
deep_copied = copy.deepcopy(original) # Creates a completely new structure
# Modify the nested mutable object
deep_copied[2].append(5)
print("Original:", original)
print("Deep Copy:", deep_copied)
print("Is the nested list the same object?", original[2] is deep_copied[2])
Original: [1, 2, [3, 4]]
Deep Copy: [1, 2, [3, 4, 5]]
Is the nested list the same object? False
The original list remains untouched. The deep copy is completely independent. This is the safest method for complex, nested objects.
Copying Custom Objects
The same principles apply to objects of your own classes. The `copy` module works with them.
By default, copy.copy() will create a new instance. It then copies the instance's `__dict__` (which holds attributes). copy.deepcopy() will recursively copy all attributes.
import copy
class MyClass:
def __init__(self, value, data):
self.value = value
self.data = data # Assume this is a list
def __repr__(self):
return f"MyClass(value={self.value}, data={self.data})"
obj = MyClass(10, [1, 2])
shallow_obj = copy.copy(obj)
deep_obj = copy.deepcopy(obj)
# Modify the nested list in the shallow copy
shallow_obj.data.append(3)
print("Original:", obj)
print("Shallow Copy:", shallow_obj)
print("Deep Copy:", deep_obj)
Original: MyClass(value=10, data=[1, 2, 3])
Shallow Copy: MyClass(value=10, data=[1, 2, 3])
Deep Copy: MyClass(value=10, data=[1, 2])
The shallow copy's `data` list is shared with the original. The deep copy's `data` is a separate list. To understand object attributes better, check our Python Object Attributes Guide for Beginners.
When to Use Each Copy Method
Use Assignment (=) when you want multiple names for the same object. Use it for aliasing, not for creating independent data.
Use Shallow Copy when your object is flat (no nested mutable objects) or when you intentionally want to share the inner objects. It is faster and uses less memory than a deep copy.
Use Deep Copy when you have a complex, nested structure and you need a completely independent clone. This is the safest option to prevent side-effects.
Conclusion
Copying in Python is not a single operation. You must choose the right tool.
Remember: Assignment shares references. Shallow copy shares nested objects. Deep copy creates full independence.
Most bugs come from using assignment or shallow copy when a deep copy is needed. Import the `copy` module. Think about your object's structure. Then choose copy.copy() or copy.deepcopy() deliberately.
This knowledge is key for working with data structures, function arguments, and object-oriented design. It helps you write predictable and bug-free Python code.