Python Different Types of Copies: Detailed Overview and Examples
In Python, copying objects can be done in several ways, depending on whether you need a shallow copy or a deep copy. Understanding these concepts is crucial for managing mutable objects and avoiding unintended side effects in your code.
Shallow Copy vs. Deep Copy
Shallow Copy
A shallow copy creates a new object but inserts references into it to the objects found in the original. It means that while the new object is distinct from the original, the contents (i.e., objects referenced) are shared.
Example: Shallow Copy with Lists
import copy
original_list = [1, [2, 3], 4]
# Create a shallow copy of the list
shallow_copied_list = copy.copy(original_list)
# Modify the nested list
shallow_copied_list[1][0] = 'changed'
print('Original list:', original_list) # Output: [1, ['changed', 3], 4]
print('Shallow copied list:', shallow_copied_list) # Output: [1, ['changed', 3], 4]
In this example, modifying the nested list in the shallow copy also affects the original list because both lists share the same nested objects.
Deep Copy
A deep copy creates a new object and recursively copies all objects found in the original, resulting in a completely independent copy. Changes to the deep copy do not affect the original object.
Example: Deep Copy with Lists
import copy
original_list = [1, [2, 3], 4]
# Create a deep copy of the list
deep_copied_list = copy.deepcopy(original_list)
# Modify the nested list
deep_copied_list[1][0] = 'changed'
print('Original list:', original_list) # Output: [1, [2, 3], 4]
print('Deep copied list:', deep_copied_list) # Output: [1, ['changed', 3], 4]
In this example, modifying the nested list in the deep copy does not affect the original list because they are completely independent.
Copying Built-in Data Types
Copying Simple Data Types
Simple data types like integers and strings are immutable, so copying them does not require special handling. Assigning them to a new variable creates a new reference.
Example: Copying Integers and Strings
a = 10
b = a # Copying integer
s1 = 'hello'
s2 = s1 # Copying string
print(a, b) # Output: 10 10
print(s1, s2) # Output: hello hello
Copying Mutable Data Types
Mutable data types like lists, dictionaries, and sets require careful handling when copying.
Example: Copying Lists
original_list = [1, 2, 3]
# Shallow copy
shallow_copied_list = original_list.copy()
# Deep copy
deep_copied_list = copy.deepcopy(original_list)
print('Original list:', original_list)
print('Shallow copied list:', shallow_copied_list)
print('Deep copied list:', deep_copied_list)
Example: Copying Dictionaries
original_dict = {'a': 1, 'b': [2, 3]}
# Shallow copy
shallow_copied_dict = original_dict.copy()
# Deep copy
deep_copied_dict = copy.deepcopy(original_dict)
# Modify the nested list
shallow_copied_dict['b'][0] = 'changed'
print('Original dict:', original_dict)
print('Shallow copied dict:', shallow_copied_dict)
print('Deep copied dict:', deep_copied_dict)
Copying Custom Objects
Implementing Custom Copy Methods
You can control how custom objects are copied by implementing __copy__
and __deepcopy__
methods.
Example: Custom Copy Methods
import copy
class CustomObject:
def __init__(self, data):
self.data = data
def __copy__(self):
# Create a shallow copy
new_copy = type(self)(self.data)
return new_copy
def __deepcopy__(self, memo):
# Create a deep copy
new_copy = type(self)(copy.deepcopy(self.data, memo))
return new_copy
# Create an instance of CustomObject
original_obj = CustomObject([1, 2, 3])
# Shallow copy
shallow_copied_obj = copy.copy(original_obj)
# Deep copy
deep_copied_obj = copy.deepcopy(original_obj)
# Modify the data in the shallow copy
shallow_copied_obj.data[0] = 'changed'
print('Original object data:', original_obj.data)
print('Shallow copied object data:', shallow_copied_obj.data)
print('Deep copied object data:', deep_copied_obj.data)
When to Use Each Type of Copy
- Shallow Copy: Use when you need a new object with the same references to mutable objects as the original.
- Deep Copy: Use when you need a completely independent copy of the original object and its nested objects.
Summary
Understanding the difference between shallow and deep copies is crucial for effective resource management and avoiding unintended side effects in your code. Shallow copies duplicate the outer container but share nested objects, while deep copies create a completely independent copy of the entire object hierarchy. By utilizing Python’s copy
module and custom copy methods, you can manage complex data structures and ensure your programs behave as expected.