Back to Blog
Python

Python Shallow Copy List: How It Works and When to Use It

python shallow copy list: Learn what a shallow copy of a Python list actually creates, how to make one, and why nested lists can still surprise you.

list copyshallow copydeep copyPython listscopy modulemutable objects
Diagram showing a shallow copy of a list where the outer list is new but elements still point to the same objects.

You have a list of dictionaries and you copy it with new_list = old_list[:]. You modify a dictionary inside new_list and the change appears in old_list. That happens because the slice created a new list, but the dictionaries inside are still the same objects. This is exactly what a python shallow copy list means: the outer container is duplicated, but the elements themselves are shared references.

What a Shallow Copy Actually Creates

A list in Python is a container of references to objects. When you perform a shallow copy, you create a new list object and populate it with the same references that the original list holds. The new list is independent in the sense that you can add or remove elements from it without affecting the original list. However, the objects those references point to are not copied. If an element is mutable, such as a dictionary, another list, or a custom object, changes to that object through either list will be visible in both.

Consider this example:

original = [{"id": 1}, {"id": 2}] shallow = original[:] shallow.append({"id": 3}) shallow[0]["id"] = 99 print(original) # [{'id': 99}, {'id': 2}] print(shallow) # [{'id': 99}, {'id': 2}, {'id': 3}]

The append operation only affects shallow, proving the list containers are separate. But the change to shallow[0]["id"] also appears in original because both lists reference the same dictionary object.

Creating a Shallow Copy with list() and Slicing

The simplest ways to create a shallow copy of a list are the slice operation and the list() constructor. Both produce a new list containing the same element references.

a = [1, 2, 3] b = a[:] # slicing c = list(a) # list constructor

These are equivalent in behavior. They work for any iterable, but when applied to a list they produce a shallow copy. The slice syntax a[:] is idiomatic and often the most readable. The list() constructor is useful when you need to convert another iterable to a list, but it also works for copying.

The copy Module and the copy() Method

Python's copy module provides a copy() function that returns a shallow copy of any object. For lists, you can also use the built-in list.copy() method, which has been available since Python 3.3.

import copy a = [1, 2, 3] b = copy.copy(a) c = a.copy()

All three approaches — slicing, list(), copy.copy(), and list.copy() — create a shallow copy. There is no practical difference between them for a list of immutable objects. The copy module becomes more important when you need to handle arbitrary objects or when you need a deep copy.

What a Shallow Copy Does Not Protect Against

A shallow copy gives you a new list, but it does not protect you from mutations of the elements inside. This is the most common source of confusion, especially with nested lists.

matrix = [[1, 2], [3, 4]] copy_matrix = matrix[:] copy_matrix[0].append(5) print(matrix) # [[1, 2, 5], [3, 4]] print(copy_matrix) # [[1, 2, 5], [3, 4]]

The inner lists are shared. Modifying one inner list affects both the original and the copy. If you need a fully independent copy, you must create a deep copy.

When a Shallow Copy Is the Right Choice

A shallow copy is often the correct choice when you want a new list that can be resized or reordered without affecting the original, but you do not intend to modify the elements themselves. For example, if you have a list of immutable objects like integers, strings, or tuples, a shallow copy is effectively independent because those objects cannot be changed in place.

names = ["Alice", "Bob", "Charlie"] names_copy = names[:] names_copy[0] = "Eve" print(names) # ['Alice', 'Bob', 'Charlie'] print(names_copy) # ['Eve', 'Bob', 'Charlie']

Reassigning an element in the copy changes the reference stored in that position, but the original list still points to the original string. Since strings are immutable, there is no way to mutate the string object itself. This makes shallow copies safe for lists of immutable elements.

Deep Copy vs Shallow Copy: When to Switch

When your list contains mutable objects and you need a fully independent copy, use copy.deepcopy(). This recursively copies the list and all objects referenced by it, creating entirely new objects for each mutable element.

import copy original = [[1, 2], [3, 4]] deep = copy.deepcopy(original) deep[0].append(5) print(original) # [[1, 2], [3, 4]] print(deep) # [[1, 2, 5], [3, 4]]

Deep copy is more expensive because it traverses the entire object graph. Use it only when you truly need to isolate the copy from mutations of nested structures. For flat lists of immutable objects, deep copy is unnecessary overhead.

Common Mistakes and How to Avoid Them

The most frequent mistake is assuming that any copy operation creates a fully independent structure. Developers often use list.copy() or slicing on a list of lists and then are surprised when nested changes leak through. Another mistake is using a shallow copy when you intend to modify the elements themselves. If you need to change the contents of a nested list without affecting the original, you must either deep copy or explicitly copy the nested structures.

A related pitfall is confusing reassignment with mutation. Reassigning an element in a shallow copy, like copy[0] = new_value, does not affect the original because it changes the reference in the copy. But mutating the object that the reference points to, such as copy[0].append(...), does. Always ask whether you are changing the list structure or the objects inside it.

Performance and Memory Implications

Shallow copy is efficient because it allocates a new list and copies only the references, not the underlying objects. The time complexity is O(n) where n is the number of elements, and the memory overhead is the size of the new list container. Deep copy, in contrast, must traverse and duplicate every reachable object, which can be significantly slower and consume much more memory for complex structures.

For lists of immutable objects, shallow copy is the optimal choice because the elements are safe to share. For lists of mutable objects, the decision depends on whether you need isolation. If you only need to protect the outer list from structural changes, shallow copy is sufficient. If you need to protect nested objects from mutation, deep copy is required, but be aware of its cost.

When performance matters, avoid deep copying large data structures unless absolutely necessary. Consider whether you can design your code to use immutable elements or to avoid mutating shared objects. In many cases, a shallow copy combined with careful handling of nested objects is the right balance between safety and efficiency.

python shallow copy list: Practical Usage and Code Examples | RYUSLOG DEV