NeuroAgent

How to Clone Lists in Python: Complete Guide

Discover why assignment doesn't create a copy in Python and master different list cloning methods including shallow copy, deep copy, and when to use each technique.

Question

How do I properly clone a list in Python to prevent unexpected changes after assignment?

When using new_list = my_list, any modifications to new_list also affect my_list. Why does this happen, and what are the correct methods to create an independent copy of a list in Python?

For example:

python
>>> my_list = [1, 2, 3]
>>> new_list = my_list
>>> new_list.append(4)
>>> my_list
[1, 2, 3, 4]

What are the different ways to clone a list in Python, and when should each method be used?

NeuroAgent

When you use new_list = my_list in Python, you’re creating another reference to the same object in memory, not an independent copy. This means any modifications to the list through one variable will affect all other references to that same list object. To properly clone a list and create an independent copy, you should use shallow copy methods like list.copy(), slicing ([:]), or the copy module’s copy() function for simple cases, and copy.deepcopy() when dealing with nested mutable objects.

Contents


Why Assignment Doesn’t Create a Copy

In Python, variables don’t store objects directly - they store references to objects in memory. When you use the assignment operator (=), you’re not creating a copy of the object; you’re simply creating another reference point to the same memory location.

Key Concept: The assignment operator, in Python, doesn’t copy. Ever. It adds a reference to an object, basically giving it a new name (as a variable), or putting that reference into a container (like a list or dictionary).

This behavior is fundamental to Python’s object model and applies to all objects, not just lists. When you modify the object through one variable, the change is visible through all other references to that same object.

python
# Both variables point to the same list object in memory
my_list = [1, 2, 3]
new_list = my_list  # This creates another reference, not a copy

# Both variables reference the same object
print(id(my_list) == id(new_list))  # True - same memory address

# Modifying through one affects the other
new_list.append(4)
print(my_list)  # [1, 2, 3, 4] - original list is modified

This reference behavior is efficient in terms of memory usage but can lead to unexpected bugs if you’re not aware of it.


Shallow Copy Methods

A shallow copy creates a new list object but the elements within the list are still references to the same objects. This is sufficient for lists containing immutable objects (like numbers, strings, tuples) but can cause issues with mutable nested objects.

Common Shallow Copy Techniques

  1. list.copy() method (Python 3.3+)

    python
    original = [1, 2, 3]
    copy = original.copy()
    
  2. Slicing [:]

    python
    original = [1, 2, 3]
    copy = original[:]
    
  3. list() constructor

    python
    original = [1, 2, 3]
    copy = list(original)
    
  4. copy.copy() function

    python
    import copy
    original = [1, 2, 3]
    copy = copy.copy(original)
    

Shallow Copy Example

python
# Shallow copy works fine with immutable elements
original = [1, 2, 3, 4]
shallow_copy = original.copy()

shallow_copy.append(5)
print(original)      # [1, 2, 3, 4] - unchanged
print(shallow_copy)   # [1, 2, 3, 4, 5] - modified

However, with nested mutable objects, shallow copying creates shared references:

python
# Problem with nested mutable objects
original = [[1, 2], [3, 4]]
shallow_copy = original.copy()

# Modifying nested list affects both
shallow_copy[0].append(5)
print(original)      # [[1, 2, 5], [3, 4]] - nested list modified!
print(shallow_copy)   # [[1, 2, 5], [3, 4]]

As Mozilla Developer Network explains, shallow copying creates a new container but populates it with references to the same original objects.


Deep Copy for Nested Structures

When you need complete independence from the original list, especially with nested mutable objects, you should use deep copying. A deep copy creates a new object and recursively copies all objects within it, ensuring no shared references.

copy.deepcopy() Function

python
import copy

original = [[1, 2], [3, 4]]
deep_copy = copy.deepcopy(original)

# Modifying nested list doesn't affect original
deep_copy[0].append(5)
print(original)      # [[1, 2], [3, 4]] - unchanged
print(deep_copy)     # [[1, 2, 5], [3, 4]] - modified independently

When Deep Copy is Necessary

Use deep copy when:

  • Your list contains nested mutable objects (lists, dictionaries, sets, custom objects)
  • You need complete isolation between the original and copied list
  • You’re working with complex data structures that might be modified independently

Deep Copy Definition: A deep copy creates a new compound object before inserting copies of the items found in the original into it in a recursive manner.


Performance and Use Case Comparison

Performance Characteristics

Method Time Complexity Memory Usage Best Use Case
Assignment (=) O(1) Minimal When you intentionally want shared references
Shallow copy O(n) Moderate Lists with immutable elements or shared nested objects
Deep copy O(n) High Lists with nested mutable objects requiring isolation

Method Selection Guide

python
# Assignment - intentional sharing
shared_data = process_data()
working_copy = shared_data  # Both variables work on same data

# Shallow copy - when elements are immutable or can be shared
numbers_list = [1, 2, 3, 4]
backup = numbers_list.copy()  # Safe for immutable elements

# Deep copy - when nested objects need isolation
complex_data = [[1, 2], {"key": "value"}, set([3, 4])]
independent_copy = copy.deepcopy(complex_data)

According to the official Python documentation, deep copy is related to nested structures: “If you have list of lists, then deepcopy copies the nested lists also, so it is a recursive copy.”


Practical Examples and Best Practices

Example 1: Simple List Copying

python
# For flat lists with immutable elements, shallow copy is sufficient
scores = [85, 92, 78, 96]
backup_scores = scores.copy()  # or scores[:]

# Modify backup doesn't affect original
backup_scores.append(100)
print(scores)          # [85, 92, 78, 96]
print(backup_scores)    # [85, 92, 78, 96, 100]

Example 2: Nested List Handling

python
# For nested mutable objects, use deep copy
student_records = [
    {"name": "Alice", "grades": [90, 85, 92]},
    {"name": "Bob", "grades": [78, 82, 88]}
]

# Shallow copy would share nested dictionaries and lists
records_copy = copy.deepcopy(student_records)

# Modify copy independently
records_copy[0]["grades"].append(95)
print(records_copy[0]["grades"])  # [90, 85, 92, 95]
print(student_records[0]["grades"])  # [90, 85, 92] - unchanged

Example 3: Function Parameters

python
def process_data(data):
    # Create a copy to avoid modifying the original
    working_data = copy.deepcopy(data)
    
    # Process the data
    working_data.append("processed")
    
    return working_data

original_data = [1, 2, 3]
result = process_data(original_data)
print(original_data)  # [1, 2, 3] - unchanged
print(result)        # [1, 2, 3, "processed"]

Best Practices

  1. Always be explicit about your copying intention

    • Use = when you want shared references
    • Use .copy() or [:] for shallow copies
    • Use copy.deepcopy() for complete isolation
  2. Consider the data structure complexity

    • Flat lists with immutable elements: shallow copy
    • Nested mutable objects: deep copy
    • Large datasets: performance implications matter
  3. Use appropriate method for your Python version

    • .copy() method available since Python 3.3
    • Slicing [:] works in all versions
    • copy.copy() and copy.deepcopy() work in all versions

As Real Python explains, “Shallow copying creates a new object but references the same nested objects, leading to shared changes. Deep copying recursively duplicates all objects, ensuring full independence from the original.”


Sources

  1. Official Python Documentation - copy module
  2. GeeksforGeeks - Deep and Shallow Copy in Python
  3. GeeksforGeeks - Cloning or Copying a List
  4. Real Python - How to Copy Objects in Python
  5. Stack Overflow - Assignment vs Shallow Copy vs Deep Copy
  6. Medium - Assignment vs Shallow Copy vs Deep Copy in Python
  7. Python Engineer - Shallow vs Deep Copying
  8. Reddit - Shallow and Deep Copy Discussion

Conclusion

Understanding Python’s object reference model is crucial for avoiding unexpected behavior when working with lists. Assignment operations create references to the same object in memory, while shallow and deep copies create new objects with different levels of independence.

Key takeaways:

  • Use assignment (=) when you intentionally want multiple variables to reference the same object
  • Use shallow copy methods (.copy(), [:], list(), copy.copy()) for lists with immutable elements or when nested objects can be safely shared
  • Use deep copy (copy.deepcopy()) when you need complete isolation, especially with nested mutable objects like lists, dictionaries, or custom objects

Practical recommendations:

  • For simple flat lists with immutable elements, shallow copying is efficient and sufficient
  • For nested data structures, always consider whether you need deep copy to prevent unintended side effects
  • Be explicit about your copying intention to make your code more maintainable and less error-prone

By choosing the appropriate copying method based on your data structure and requirements, you can prevent unexpected changes and write more reliable Python code.