2D Lists in Python

Pitfalls to avoid when creating 2d lists in Python.

For on 1d lists, see this blog post.

Source

Find source code on GitHub.

Background

A 2d-list in Python can be conceptualized as (row, column) pairs:

(0, 0) (0, 1) (0, 2)
(1, 0) (1, 1) (1, 2)
(2, 0) (2, 1) (2, 2)

Note that row and column are the opposite of traditional x-y coordinate systems!

Rows count along the -y axis and are listed first. Columns count along the +x axis and are listed second. (Alternatively, you can do a coordinate rotation in your head: +x is down, +y is right).

Python will represent a 2d-list as a list of lists:

[[list], [list], ...]

Each list element is itself a list. For example,

[   # <-- Outer list
    [00, 01, 02],   # <-- Inner list
    [10, 11, 12],
    [20, 21, 22],
]

Syntax

for Loop with Multiplication

This Python FAQ recommends creating an M x N list in two steps:

  • create a list of M empty rows
  • fill each row with N columns (in a list)

Specifically,

the_list = [None] * nrows
for i in range(nrows):
    the_list[i] = [value] * ncols

Nested List Comprehension

A 2d list can be created with list comprehensions:

[[value for _ in range(ncols)] for _ in range(nrows)]

In detail, create a list of columns using:

# List of columns
[value for _ in range(ncols)]

We can create rows from these columns using this form:

# List of rows, column unspecified
[column for _ in range(nrows)]

Substituting for the column gives the original comprehension.

List Comprehension with Multiplication

Multiplication can be used for the inner list comprehension (list of columns):

[[value] * ncols for _ in range(nrows)]

Do not use multiplication for the outer comprehension (list of rows)! See Common Mistakes for details.

Common Mistakes

The following will give unexpected results!

# Do not do this!
[[value] * ncols] * nrows

The * operator creates a reference to the original object, not a new object. This means that:

  • The basic type (say, None) is referenced multiple times (fine)
  • A single copy of the list [None, None, ...] is referenced multiple times (bad!)

For example,

[None] * ncols

creates a list of references to the None type:

[None, None, ...]

The problem arises when this is used to populate the rows of a 2d list. This expression:

[None, None, ...] * nrows

creates a list of references to the same list:

[[None, None, ...], [None, None, ...], ...]

Changing a single value makes this apparent:

the_list = [[None] * 2] * 3
# [[None, None], [None, None], [None, None]]
the_list[0][0] = 42
# [[42, None], [42, None], [42, None]]

Further Reading

Python docs:

Stack Overflow:

Numpy offers an N-dimensional array.