Lists of lists

Introduction

A list can hold other lists. That gives you tables, grids, game boards and matrices: a list of rows, where each row is a list of values.

Game boards, spreadsheets, images and timetables are all tables. Python has no special table type, because a list of lists does the job, and it follows the same rules you already know.

How it works

A game board

board = [["."] * 3 for _ in range(3)]
board[1][1] = "X"
board[0][2] = "O"
for row in board:
    print(" ".join(row))
Open in compiler

A comprehension builds three separate rows. Setting one cell changes only that row, and " ".join(row) prints each row as text.

The copying trap

bad = [[0] * 2] * 3
bad[0][0] = 5
print(bad)
good = [[0] * 2 for _ in range(3)]
good[0][0] = 5
print(good)
Open in compiler

[[0] * 2] * 3 does not make three rows: it makes three references to the same row. The objects panel shows three arrows pointing to one inner list, so changing bad[0][0] changes every row. The comprehension version creates a new inner list each time, which is what you almost always want.

Syntax

grid = [[1, 2, 3], [4, 5, 6]]
grid[1][2]                       # row 1, column 2
for row in grid:
    print(sum(row))
empty = [[0] * 3 for _ in range(2)]

Example

grid = [[1, 2, 3], [4, 5, 6]]
print(grid[1][2])
grid[0][0] = 9
for row in grid:
    print(sum(row))
Open in compiler

grid[1][2] means "row 1, then position 2 in that row", which is 6. In the objects panel, grid points to an outer list whose two cells each point to an inner list. After grid[0][0] = 9 the first row is [9, 2, 3], so the row sums are 14 and 15.

Line by line

  1. grid is a list holding two lists.
  2. grid[1][2] goes to row 1 and then to position 2 in that row, which is 6.
  3. grid[0][0] = 9 changes one cell of the first row.
  4. for row in grid: gives one inner list on each pass.
  5. sum(row) adds up that row, giving 14 and then 15.

Expected result

6
14
15

In the real world

An image is a grid of pixels, and a chess board is a grid of squares. Numerical libraries such as NumPy store grids far more efficiently, but they use the same row-and-column thinking.

Common mistakes

  • [[0] * n] * m for a grid. Use [[0] * n for _ in range(m)].
  • Mixing up the order: grid[row][col], not grid[col][row].
  • Copying a grid with grid.copy(): it copies the outer list only; the rows are still shared.

Best practices

  • Always build grids with a comprehension.
  • Decide once whether you write row first, and stay with it.
  • Use for row in grid when you do not need the positions.

Summary

A list of lists makes a table, and grid[row][col] reaches one cell. Multiplying a list of lists repeats references to the same row, so build grids with a comprehension.

Practice

Build a 3 × 3 multiplication table as a list of lists, then print the sum of its diagonal (grid[i][i]).

Quick check

1. With grid = [[1, 2], [3, 4]], what is grid[1][0]?
2. Why does [[0] * 2] * 3 behave strangely?
3. How many items does len([[1, 2, 3], [4, 5, 6]]) count?

Key takeaways

  • grid[row][col] reads one cell.
  • [[0] * n for _ in range(m)] makes separate rows.
  • [[0] * n] * m repeats a reference to one row.

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.