copy
The copy module provides two functions for creating copies of Python objects: copy() for shallow copies and deepcopy() for deep copies. Understanding the difference between these two is essential when working with mutable objects like lists, dictionaries, and custom objects.
Shallow Copy with copy()
A shallow copy creates a new object but inserts references to the original elements. For immutable objects like strings and numbers, this doesn’t matter. But for nested mutable objects, both the original and the copy share the same inner objects.
import copy
# Original list with nested list
original = [[1, 2], [3, 4]]
# Shallow copy
shallow = copy.copy(original)
# Modify the nested list in the copy
shallow[0][0] = 99
print(original)
# [[99, 2], [3, 4]]
print(shallow)
# [[99, 2], [3, 4]]
The inner lists are shared between original and shallow.
Creating shallow copies
# Three ways to create a shallow copy
list_copy = copy.copy(original_list)
list_copy = original_list[:] # slice notation
list_copy = list(original_list) # constructor
Deep Copy with deepcopy()
A deep copy creates a completely independent copy of an object and all objects it contains recursively. Changes to the deep copy never affect the original.
import copy
original = [[1, 2], [3, 4]]
# Deep copy
deep = copy.deepcopy(original)
# Modify the deep copy
deep[0][0] = 99
print(original)
# [[1, 2], [3, 4]]
print(deep)
# [[99, 2], [3, 4]]
copy() vs deepcopy()
| Feature | copy.copy() | copy.deepcopy() |
|---|---|---|
| Performance | Faster | Slower |
| Nested objects | Shared | Independent |
| Circular references | May cause issues | Handled correctly |
| Memory | Less | More |
Parameters
copy(object)
| Parameter | Type | Description |
|---|---|---|
object | any | The object to copy (must be copyable) |
deepcopy(object, memo=None)
| Parameter | Type | Description |
|---|---|---|
object | any | The object to copy recursively |
memo | dict | Optional dictionary for handling circular references |
Common Patterns
Copying a dictionary
import copy
original = {"name": "Alice", "scores": [90, 85, 92]}
# Shallow copy — nested list is shared
shallow = copy.copy(original)
shallow["scores"].append(100)
print(original["scores"]) # [90, 85, 92, 100]
# Deep copy — completely independent
deep = copy.deepcopy(original)
deep["scores"].append(88)
print(original["scores"]) # [90, 85, 92, 100]
Copying a custom object
import copy
class Person:
def __init__(self, name, hobbies):
self.name = name
self.hobbies = hobbies # mutable list
def __repr__(self):
return f"Person({self.name}, {self.hobbies})"
original = Person("Alice", ["reading", "cycling"])
# Shallow copy — hobbies list is shared
shallow = copy.copy(original)
shallow.hobbies.append("hiking")
print(original.hobbies) # ['reading', 'cycling', 'hiking']
# Deep copy — hobbies are independent
deep = copy.deepcopy(original)
deep.hobbies.append("swimming")
print(original.hobbies) # ['reading', 'cycling', 'hiking']
Handling circular references
import copy
class Node:
def __init__(self, name):
self.name = name
self.children = []
def add_child(self, node):
self.children.append(node)
# Create circular reference
root = Node("root")
child = Node("child")
root.add_child(child)
child.add_child(root) # circular!
# deepcopy handles this correctly
new_root = copy.deepcopy(root)
print(new_root.children[0].children[0] is new_root) # True
When to Use Each
Use copy() when:
- The object contains only immutable types
- You need better performance
- You intentionally want nested objects shared
Use deepcopy() when:
- The object contains mutable nested objects
- You need complete independence from the original
- You’re working with complex data structures like graphs or trees