Last modified: Mar 25, 2026 By Alexander Williams
Python Copy Array: Shallow vs Deep Copy Guide
Copying an array in Python seems simple. But it can cause big problems. A wrong copy can change your original data. This leads to bugs that are hard to find.
This guide explains how to copy arrays correctly. You will learn the difference between a shallow copy and a deep copy. We will cover the copy module and the list() constructor.
Why Copying Arrays is Tricky
In Python, variables are names that point to objects. When you assign one variable to another, you are not creating a new object. You are creating a new name for the same object.
This is called aliasing. Both variables refer to the same list in memory. A change through one variable affects the other.
# Example of aliasing - the wrong way to "copy"
original_list = [1, 2, 3]
alias_list = original_list # This is NOT a copy
print("Original list before change:", original_list)
print("Alias list before change:", alias_list)
alias_list.append(4) # Modifying the alias
print("\nOriginal list after change:", original_list) # Also changed!
print("Alias list after change:", alias_list)
Original list before change: [1, 2, 3]
Alias list before change: [1, 2, 3]
Original list after change: [1, 2, 3, 4]
Alias list after change: [1, 2, 3, 4]
As you see, appending to alias_list also changed original_list. This is often not what you want. You need a proper copy.
Method 1: The Slice Operator [:]
The slice operator [:] is a common way to copy a list. It creates a new list containing all elements from the original. This is a shallow copy.
For a simple list of numbers or strings, this works perfectly. The new list is independent.
# Creating a shallow copy using slicing
original = [10, 20, 30]
shallow_copy = original[:] # The [:] slice copies all elements
print("Original:", original)
print("Shallow Copy:", shallow_copy)
shallow_copy[0] = 99 # Modify the copy
print("\nAfter modifying the copy:")
print("Original:", original) # Unchanged
print("Shallow Copy:", shallow_copy) # Changed
Original: [10, 20, 30]
Shallow Copy: [10, 20, 30]
After modifying the copy:
Original: [10, 20, 30]
Shallow Copy: [99, 20, 30]
The original list stayed safe. For more on slicing, see our Python Array Slice Guide.
Method 2: The list() Constructor
You can use the list() built-in function. Pass the original list as an argument. It creates a new list with the same elements. This is also a shallow copy.
# Creating a shallow copy using list()
original = ['a', 'b', 'c']
list_copy = list(original) # Creates a new list object
print("Original:", original)
list_copy.append('d')
print("After appending to copy:")
print("Original:", original) # Still ['a', 'b', 'c']
print("Copy:", list_copy) # Now ['a', 'b', 'c', 'd']
Original: ['a', 'b', 'c']
After appending to copy:
Original: ['a', 'b', 'c']
Copy: ['a', 'b', 'c', 'd']
This method is very clear and readable. It's excellent for simple, flat lists.
The Problem with Shallow Copies
Shallow copies only copy one level deep. If your list contains other mutable objects (like other lists), those inner objects are not copied. The copy gets new references to the same inner objects.
This leads to shared data at the second level.
# Demonstrating the shallow copy problem
original = [1, 2, [3, 4]] # A list containing another list
shallow_copy = original[:] # Shallow copy
print("Original inner list ID:", id(original[2]))
print("Copy's inner list ID:", id(shallow_copy[2]))
# Modifying the inner list through the copy
shallow_copy[2].append(5)
print("\nAfter modifying inner list in copy:")
print("Original:", original) # The inner list is changed!
print("Copy:", shallow_copy)
Original inner list ID: 140241818390976
Copy's inner list ID: 140241818390976
After modifying inner list in copy:
Original: [1, 2, [3, 4, 5]]
Copy: [1, 2, [3, 4, 5]]
Both original[2] and shallow_copy[2] point to the same list object. Changing it affects both "copies". This is a major pitfall.
Method 3: Deep Copy with the copy Module
To solve the nested object problem, you need a deep copy. A deep copy creates a new object and recursively copies all objects found within it.
Python provides the copy module. Use copy.deepcopy().
import copy
# Creating a deep copy
original = [1, 2, [3, 4]]
deep_copy = copy.deepcopy(original) # True independent copy
print("Original inner list ID:", id(original[2]))
print("Deep copy's inner list ID:", id(deep_copy[2]))
# Modifying the inner list in the deep copy
deep_copy[2].append(5)
print("\nAfter modifying inner list in deep copy:")
print("Original:", original) # Unchanged!
print("Deep Copy:", deep_copy) # Changed independently
Original inner list ID: 140050699924544
Deep copy's inner list ID: 140050699924800
After modifying inner list in deep copy:
Original: [1, 2, [3, 4]]
Deep Copy: [1, 2, [3, 4, 5]]
The deepcopy function created a completely independent copy. The inner lists are different objects. Changes are isolated.
The module also has copy.copy() for making explicit shallow copies. It's useful for clarity in your code.
Choosing the Right Copy Method
How do you decide which method to use? Follow this simple rule.
Use a shallow copy when your list contains only immutable data types. Examples are integers, floats, strings, or tuples. The list() constructor or slice [:] is perfect.
Use a deep copy when your list contains other mutable objects. Examples are nested lists, dictionaries, or custom class instances. Always use copy.deepcopy() in this case.
Understanding the structure of your data is key. If you are unsure about the Python Array vs List distinction, our guide on Python Array vs List: Key Differences Explained can help.
Copying Arrays from the array Module
Python has a dedicated array module for efficient numeric arrays. The same principles apply.
You can use the copy() method of the array object. It returns a shallow copy of the array.
from array import array
# Creating and copying a typed array
original_arr = array('i', [5, 10, 15, 20]) # 'i' for signed integers
copied_arr = original_arr.copy() # The array's own copy method
print("Original array:", original_arr)
copied_arr[1] = 999
print("\nAfter modifying copied array:")
print("Original array:", original_arr) # Unchanged
print("Copied array:", copied_arr)
Original array: array('i', [5, 10, 15, 20])
After modifying copied array:
Original array: array('i', [5, 10, 15, 20])
Copied array: array('i', [5, 999, 15, 20])
The .copy() method is clean and efficient for array objects. For more on creating arrays, see the Python Array Implementation Guide.
Common Mistakes and Best Practices
Avoid the assignment operator = for copying. It only creates an alias.
Always think about the depth of your data structure. Ask: "Does my list contain other lists or dictionaries?" If yes, use a deep copy.
For performance, shallow copies are faster and use less memory. Use them when safe. Deep copies are slower but necessary for complex, nested data.
Test your copies. Modify the copy and check if the original changes. This simple test can save hours of debugging.
Conclusion
Copying arrays in Python requires careful thought. The assignment operator does not create a copy.
For simple, flat lists, use the slice operator [:] or the list() function. These create shallow copies.
For lists containing other mutable objects, you must use copy.deepcopy(). This creates a true, independent duplicate.
Remember the rule: shallow for flat data, deep for nested data. Choosing the right method prevents subtle bugs and keeps your data safe.
Mastering copying is a fundamental skill for effective Python programming. It ensures your data manipulations are predictable and correct.