Python DISCUSSION

Why does changing one row of [[0] * 3] * 3 change every row in my Python grid?

Started by su nested listslist aliasingshallow copydeepcopylist comprehension
4 replies 248 views 5 participants
Latest activity · 30 Sep 2026

Why does changing one row of [[0] * 3] * 3 change every row in my Python grid?

su Python Forum
#1

I create a 3×3 grid for a small LED matrix simulation with grid = [[0] * 3] * 3. Setting one cell with grid[0][1] = 1 gives [[0, 1, 0], [0, 1, 0], [0, 1, 0]]: the whole column changes. A second surprise in the same script: after backup = grid, changes to grid also appear in backup.

Why do both happen, and what is the correct way to build a 2D list and to make an independent copy of one?

Community replies 4

Re: Why does changing one row of [[0] * 3] * 3 change every row in my Python grid?

#2

Python variables and list slots hold references to objects, not the objects themselves. [0] * 3 builds a list with three references to the integer 0, which is harmless because integers are immutable. [row] * 3 builds an outer list with three references to the same inner list; nothing is copied. So grid[0], grid[1] and grid[2] are one object, as grid[0] is grid[1] confirms. grid[0][1] = 1 mutates that single row and you see it three times.

Build the rows separately: grid = [[0] * 3 for _ in range(3)]. The comprehension evaluates [0] * 3 afresh on each iteration, giving three distinct lists.

Re: Why does changing one row of [[0] * 3] * 3 change every row in my Python grid?

#3

backup = grid is the same story: assignment never copies, it binds another name to the same object. There are two levels of copying. grid.copy(), grid[:] and list(grid) make a shallow copy: a new outer list whose slots still refer to the same row objects. Appending a row to the copy leaves the original alone, but backup[0][1] = 9 still changes both.

For a fully independent copy of nested data use copy.deepcopy(grid). For exactly two levels, [row[:] for row in grid] does the same job and is faster.

Re: Why does changing one row of [[0] * 3] * 3 change every row in my Python grid?

#4

Function arguments follow the same rule: the function receives references to the caller's objects. def clear(g): g[0][0] = 0 modifies the caller's grid. def reset(g): g = [] does not, because it only rebinds the local name. Mutation through a reference is visible to everyone holding that reference; rebinding a name affects only that name.

+= sits on both sides of this. On a list, a += [1] mutates in place like extend, so other references see the change, whereas a = a + [1] creates a new list and rebinds a. On immutable types such as int, str and tuple, += always rebinds.

Re: Why does changing one row of [[0] * 3] * 3 change every row in my Python grid?

#5

For numeric grids of any real size, use NumPy: grid = numpy.zeros((3, 3), dtype=int) is one block of memory indexed as grid[0, 1], with no shared-row problem. It has an aliasing rule of its own, though: basic slices are views, not copies. After row = grid[0], assigning row[1] = 5 changes grid; use grid[0].copy() when you need independence.

When debugging, remember that is tests identity and == tests equal contents. With the broken grid both grid[0] == grid[1] and grid[0] is grid[1] are True; with the correct one the first is True and the second False. On MicroPython, a flat bytearray(9) indexed as buf[r * 3 + c] is the compact alternative.

TEP COMMUNITY