Python
lists
linked-lists
programming
troubleshooting

Why does this code for initializing a list of lists apparently link the lists together?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Many Python developers are surprised when updating one row in a matrix updates all rows at once. The issue usually appears after creating a list of lists with multiplication, which reuses references to the same inner list. Understanding object references and mutability makes this behavior predictable and easy to avoid.

The Root Cause: Repeated References, Not Repeated Lists

Consider this common initialization:

python
rows = [[0] * 3] * 4
rows[0][1] = 99
print(rows)

Output:

text
[[0, 99, 0], [0, 99, 0], [0, 99, 0], [0, 99, 0]]

The expression creates one inner list and repeats its reference four times. Every row points to the same object.

You can confirm with id values:

python
rows = [[0] * 3] * 4
for i, r in enumerate(rows):
    print(i, id(r))

All row ids will match, proving aliasing.

Correct Initialization with Independent Inner Lists

Use a list comprehension so each row is constructed independently.

python
rows = [[0] * 3 for _ in range(4)]
rows[0][1] = 99
print(rows)

Now output becomes:

text
[[0, 99, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]

Each row has a different object identity.

Why This Happens in Python Terms

Python lists store references to objects. List multiplication on a list containing mutable objects duplicates references, not deep copies of contained objects. With immutable elements such as integers, this is often harmless. With mutable nested objects, aliasing effects appear quickly.

This behavior is consistent and intentional. It keeps multiplication fast and memory efficient for many use cases.

Safe Patterns for 2D Data

Depending on your use case, these patterns are robust:

  • matrix with list comprehension for independent rows
  • copy.deepcopy when cloning an existing nested structure
  • NumPy arrays for numeric matrix operations and vectorized computation

Example cloning safely:

python
1import copy
2
3original = [[1, 2], [3, 4]]
4clone = copy.deepcopy(original)
5clone[0][0] = 99
6
7print("original", original)
8print("clone", clone)

deepcopy recursively creates new nested objects, preventing shared inner references.

When Shared Inner Lists Are Actually Useful

Sometimes aliasing is intentional. For example, you may want many keys to share one mutable configuration object. In such cases, document the intent clearly so future maintainers do not misinterpret behavior as a bug.

python
1shared = {"enabled": True}
2items = [shared] * 3
3items[2]["enabled"] = False
4print(items)

All elements reflect the same underlying dictionary by design.

Debugging Checklist for Suspected Aliasing

If nested structures behave unexpectedly, run a quick check:

  • print element ids for nested lists or dicts
  • mutate one nested element and observe whether siblings change
  • inspect initialization code for multiplication on mutable objects
  • validate copy semantics when passing nested data between functions

This fast workflow catches most reference bugs in minutes.

Performance Notes

List comprehension allocates more objects than multiplication, but for most application-level data sizes the correctness tradeoff is worth it. If you process large numerical matrices, NumPy is usually faster and more memory efficient than nested Python lists.

For non-numeric nested structures, prefer clarity first. The time saved in debugging generally outweighs small allocation differences.

Common Pitfalls

The most common pitfall is assuming [[0] * m] * n behaves like a mathematical matrix constructor. It does not create independent rows. Another issue is using shallow copy on nested structures and expecting full independence. Developers also forget that aliasing can be introduced indirectly when default mutable arguments or reused objects are passed into constructors. Finally, debugging becomes harder when row updates happen far away from initialization, so write explicit initialization code near data structure creation.

Summary

  • List multiplication with mutable inner objects duplicates references.
  • Use list comprehensions for independent nested lists.
  • Confirm aliasing by comparing object ids.
  • Use deepcopy when cloning already nested mutable structures.
  • Keep intentional shared-reference patterns explicit and documented.

Course illustration
Course illustration

All Rights Reserved.