Explain Codes LogoExplain Codes Logo

Python list by value not by reference

python
deep-copy
list-comprehension
mutable-objects
Anton ShumikhinbyAnton Shumikhin·Mar 3, 2025
TLDR

To clone a list's values, either use the list constructor or slicing, thus avoiding reference-based duplicates.

List constructor example:

original = [1, 2, 3] cloned = list(original) # Copy list value, not just its charming personality.

Slicing example:

original = [1, 2, 3] cloned = original[:] # Just a slice of life, err... list.

For nested lists or complex objects, use copy.deepcopy() for complete independence.

Deep copy:

import copy original = [[1], [2], [3]] cloned = copy.deepcopy(original) # Go deeper. We need to copyception.

Knowing when to shelf the deep copy

While copy.deepcopy() creates fully autonomous list copies (think of them as independent list twins), it can be a resource hog for large or deeply nested structures. It's a good choice when your list contains nested mutable objects and you want the changes in those entities not to reflect in the original list.

A step beyond basic copying

For multi-dimensional lists or other nested structures, basic shallow copy might not cut it as it just duplicates references to nested objects, not the actual objects. A list comprehension allows you to create a deeper copy:

original = [[1], [2], [3]] cloned = [item[:] for item in original] # This comprehension goes a long way. Or deep way, to be accurate.

Balance between making a deep copy and a targeted copy using list comprehension or manual copying, especially when dealing with complex structures.

What you see is not what you always get

With mutable objects, choosing the right copying method becomes critical. A shallow copy, made with slicing or list(), won't protect changes in mutable objects within the list from being mirrored in the copy. Assess the mutability of objects inside the list to choose the right copying mechanism.

Efficiency outranks others

b.extend(a) is your go-to method for extending one list with another without distorting the original, in terms of efficiency. The run-time efficiency comparison is:

b.extend(a) > a[:] > deepcopy  # The survival of the fastest.

Match your method to your performance and code behavior requirements.

Keeping function spaces clean

Python hands a reference to the list when you pass it to a function, not a duplicate. To protect the original from any function-incurred modifications, explicitly pass a copy:

def process_list(l): local_copy = l[:] # Operate locally. Think globally. # ... do some work ... original_list = [1, 2, 3] process_list(original_list) # Let's see any side effects. Just kidding! There will be none.

original_list stays unaltered post the function call, resonating with the concept of immutable function design.