Why does [[0]*3]*3 break?

Multiplying a list repeats the reference, not the object. [[0]*3]*3 builds one inner list and points at it three times, so writing to one row writes to all of them. Use a comprehension — [[0]*3 for _ in range(3)] — which evaluates the inner expression once per row.

Overview

What multiplication actually does

[x] * 3 builds a list holding the same reference three times. For immutable contents that is invisible — [0] * 3 is fine, because you can never mutate a 0. For a mutable inner object it is a trap, because all three names lead to one object.

The tell is that the bug only appears on write. Building and printing the grid looks perfect; the first assignment to a cell is when it falls apart.

Lists & arraysConceptualMedium

Step through it

What to watch

  • All three rows share one address in the broken version.
  • One write appears in three places — nothing was copied.
  • The comprehension produces three distinct objects.

Say this out loud

"List multiplication copies references. There's one inner list and three pointers to it. A comprehension builds a fresh row each time, which is what you want."

Why does [[0]*3]*3 break?

What does [[0] * 3] * 3 create, and why does writing to one row change all of them?

The same idea, three ways

This is one instance of a general rule: assignment and shallow copies duplicate references, never the objects behind them.

b = a — another name for the same list. b = a[:] or list(a) or copy.copy(a) — a new outer list holding the same inner references, so nested contents are still shared. copy.deepcopy(a) — new objects all the way down, at the cost of walking the whole structure.

How to spot it in review

Any * n where the repeated element is a list, a dict, a set or a custom object is suspicious. The same rule explains the mutable default argument bug: def f(acc=[]) creates the list once, when the function is defined, and every call that omits the argument shares it.

The fix in both cases is the same — build the mutable thing at the moment it is needed rather than once, up front.

Run it in Python

The broken grid and the correct one side by side with their row identities printed, then the three levels of copying, then the mutable-default-argument bug that has the same cause.

aliasing.pyPython 3
Output

How the code works

  1. [hex(id(row)) for row in wrong]Three identical addresses. That single line is the whole explanation, and it is worth printing in an interview rather than describing.
  2. [[0] * 3 for _ in range(3)]The comprehension evaluates [0] * 3 once per iteration, so each row is a separate object. [0] * 3 itself is safe because integers cannot be mutated.
  3. shallow = original[:]A new outer list holding the same inner references. Appending to original does not affect it; mutating a nested list does. Both behaviours are shown.
  4. def broken(item, accumulated=[])The default is evaluated once, when the function is defined, so every call that omits the argument shares one list. Same cause: a mutable object created once and referenced many times.

Change one thing

  • Build the grid with [[0] * 3] * 3 and only ever read from it. It looks completely correct — the bug needs a write.
  • Replace deepcopy with copy.copy and re-run. The nested mutation reappears, which is the difference between the two in one line.

Where this runs

Real CPython, compiled to WebAssembly and running on your own machine — nothing is uploaded. The first run takes a few seconds while the interpreter downloads; after that it is immediate. Need more room, or want to paste your own attempt? Use the Python compiler.

Check yourself

0 of 3

Answer without scrolling back up.

  1. [[0] * 3] * 3 creates:

  2. a[:] on a list of lists gives you:

  3. def f(x, acc=[]) misbehaves because the default is evaluated:

Cheat sheet

Why does [[0]*3]*3 break?

Multiplying a list repeats the reference, not the object. [[0]*3]*3 builds one inner list and points at it three times, so writing to one row writes to all of them. Use a comprehension — [[0]*3 for _ in range(3)] — which evaluates the inner expression once per row.

INTERVIEW · vizlearn.in/interview/the-nested-list-multiplication-bug.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.