Back to Blog
Python

Python List Copy Shallow: What It Does and When to Use It

python list copy shallow: Understand what a shallow copy of a Python list does, how to create one, and when it's the right choice for your code.

shallow copylist copyingnested listscopy modulePython data structures
Diagram showing a shallow copy of a Python list sharing nested list objects with the original list

When you assign a list to a new variable, Python does not copy the list; it creates a new reference to the same object. To get an independent list, you need an explicit copy. The most common request is a shallow copy, which duplicates the outer list but shares the inner objects. Understanding python list copy shallow behavior is essential for avoiding subtle bugs when working with nested data.

What a Shallow Copy of a Python List Actually Does

A shallow copy creates a new list object and populates it with references to the same elements as the original. For a list of immutable objects like integers or strings, this is indistinguishable from a deep copy because the elements themselves cannot be modified in place. The difference appears when the list contains mutable objects, such as other lists or dictionaries. In that case, the shallow copy shares those nested objects with the original, so changes to a nested object affect both lists.

original = [[1, 2], [3, 4]] shallow = original.copy() shallow[0].append(99) print(original) # [[1, 2, 99], [3, 4]] print(shallow) # [[1, 2, 99], [3, 4]]

The outer list is new, but the inner lists are the same objects. This is the core behavior of any shallow copy method in Python.

Common Ways to Create a Shallow Copy

Python provides several idiomatic ways to make a shallow copy of a list. Each has the same underlying semantics but differs in readability and intent.

Using the copy() Method

The list type has a built-in copy() method that returns a shallow copy. It is the most explicit and readable choice for most code.

items = [1, 2, [3, 4]] copied = items.copy()

Using List Slicing with [:]

The slice operator with no bounds creates a new list containing all elements. This is a concise idiom that many Python developers recognize.

items = [1, 2, [3, 4]] copied = items[:]

Using list() Constructor

Passing the original list to the list() constructor produces a new list with the same elements. This works for any iterable, not just lists.

items = [1, 2, [3, 4]] copied = list(items)

Using copy.copy() from the copy Module

The copy module provides a generic copy() function that works for any object. For lists, it performs a shallow copy. This is useful when you are writing code that must handle different container types.

import copy items = [1, 2, [3, 4]] copied = copy.copy(items)

All four approaches produce a new list with shared inner objects. The choice is mostly about style and context.

How Nested Objects Reveal Shallow Copy Behavior

To see the difference between shallow and deep copy, you need a list that contains at least one mutable object. Consider a list of lists used as a matrix. A shallow copy gives you a new outer list but the same row objects. Modifying a row through the copy changes the original.

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

If you need the copy to be fully independent, you must use a deep copy from the copy module. Deep copy recursively duplicates all nested objects, creating a completely separate data structure.

import copy matrix = [[1, 2], [3, 4]] deep_copy = copy.deepcopy(matrix) deep_copy[0][0] = 100 print(matrix) # [[1, 2], [3, 4]]

Deep copy is more expensive and can fail if the object graph contains uncopyable resources like file handles or network connections. Shallow copy is often sufficient when you only need to reorder or replace elements at the top level.

When a Shallow Copy Is the Wrong Choice

A shallow copy is the right choice when you need a new list but are okay with sharing the contained objects. This is common when you want to sort or filter without mutating the original list, but the elements themselves are immutable or you do not plan to modify them in place.

However, a shallow copy becomes a liability when you need to modify nested structures without affecting the original. For example, if you are building a data pipeline that transforms nested dictionaries, a shallow copy will let those changes leak back into the source data. In such cases, use copy.deepcopy() or construct new objects explicitly.

Another scenario where shallow copy is insufficient is when you are implementing a snapshot of a complex state that will be mutated later. A shallow snapshot will not protect you from changes to nested objects.

Performance and Memory Characteristics of Shallow Copies

Creating a shallow copy is O(n) in the length of the list because it must allocate a new list and copy the references. The memory footprint is proportional to the number of elements, but the nested objects are not duplicated. This makes shallow copy fast and memory-efficient for large lists of mutable objects, provided you do not need independent nested structures.

Deep copy is significantly more expensive because it traverses the entire object graph and duplicates every mutable object. For a list of lists, deep copy copies each inner list, leading to O(total number of elements) memory and time. If the list contains many nested levels, the cost can be substantial.

When performance matters, consider whether you actually need deep copy. Often, a shallow copy combined with careful handling of nested objects is sufficient. For read-only access to nested data, shallow copy is ideal.

Choosing the Right Copy Method for Your Use Case

The decision between shallow and deep copy depends on what you intend to do with the new list. Use a shallow copy when you need to:

  • Reorder or replace top-level elements without affecting the original.
  • Pass a list to a function that might reassign elements but not modify nested objects.
  • Create a quick snapshot of a list where the elements are immutable.

Use a deep copy when you need to:

  • Modify nested objects without affecting the original data.
  • Store a historical state that must remain unchanged.
  • Work with data structures that contain mutable objects and require full isolation.

For most everyday list operations, a shallow copy is the default and correct choice. It is fast, idiomatic, and avoids the overhead of deep copying. The key is to be aware of the shared nested objects and to switch to deep copy only when the situation demands it.

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