Python Nested List Comprehensions Explained (Python Docs 5.1.4) | Matrix Transposition, zip() & Beginner Guide

๐Ÿ“– Based on Python 3 official docs ยง5.1.4 โ€” explained like a friend, with extra examples added
Python Intermediate Data Structures Official Docs Updated June 2026

This page explains Python Nested List Comprehensions (Python Docs 5.1.4) in simple language. A nested list comprehension is a list comprehension that has another list comprehension as its expression โ€” it's the most concise way to work with 2D data like matrices in Python.

The docs use matrix transposition as the main example. By the end of this guide you'll understand what nested comprehensions actually do under the hood, how they compare to equivalent nested loops, and when to use zip() instead.

What is a Python Nested List Comprehension?

A regular list comprehension creates a flat list. A nested list comprehension has another list comprehension as its expression โ€” so it creates a list of lists. This is exactly what you need for 2D structures like matrices, grids, or tables.

From the Python docs

The initial expression in a list comprehension can be any arbitrary expression, including another list comprehension.

The syntax looks like this:

Python
# Structure of a nested list comprehension
[[inner_expression for item in inner_iterable] for item in outer_iterable]
#  โ†‘ inner comprehension โ†‘                       โ†‘ outer for loop โ†‘

# The outer for loop runs first, producing one row per iteration.
# For each row, the inner comprehension builds a list.

The Matrix Example โ€” From the Python Docs

The Python docs use a 3ร—4 matrix to demonstrate nested comprehensions. A 3ร—4 matrix has 3 rows and 4 columns:

PythonPython docs example
matrix = [
    [1,  2,  3,  4],   # row 0
    [5,  6,  7,  8],   # row 1
    [9, 10, 11, 12],   # row 2
]

Transposing means flipping rows and columns โ€” row 0 becomes column 0, row 1 becomes column 1, and so on. Watch the highlighted numbers: the first row of the original matrix becomes the first column of the transposed matrix.

Original Matrix (3 Rows ร— 4 Columns)
Columns โ†’
1
2
3
4
5
6
7
8
9
10
11
12
Transpose โ†“
๐Ÿ’ก Notice: Each row in the original matrix becomes a column in the transposed matrix.
Transposed Matrix (4 Rows ร— 3 Columns)
Columns โ†’
1
5
9
2
6
10
3
7
11
4
8
12
PythonPython docs example
# Transpose with a nested list comprehension
[[row[i] for row in matrix] for i in range(4)]
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Three Equivalent Ways to Transpose โ€” All from the Python Docs

The docs show the same operation three ways, from the most verbose to the most concise. Understanding all three is key to really getting nested comprehensions:

Version 1Nested list comprehension (most concise)
PythonPython docs example
[[row[i] for row in matrix] for i in range(4)]
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
Version 2Outer loop + inner comprehension
PythonPython docs example
transposed = []
for i in range(4):
    transposed.append([row[i] for row in matrix])

transposed
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
Version 3Fully expanded nested loops (most explicit)
PythonPython docs example
transposed = []
for i in range(4):
    transposed_row = []
    for row in matrix:
        transposed_row.append(row[i])
    transposed.append(transposed_row)

transposed
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

All three give identical results. Version 1 is the most Pythonic, but Version 3 is the easiest to trace through step by step if you're learning.

How the Inner Comprehension Evaluates โ€” Step by Step

This is the part that confuses most beginners. The inner comprehension doesn't run independently โ€” it's evaluated in the context of the outer for loop.

From the Python docs

The inner list comprehension is evaluated in the context of the for that follows it.

01

The outer loop runs first: for i in range(4) gives i = 0, then 1, then 2, then 3.

02

For each value of i, the inner comprehension [row[i] for row in matrix] runs completely โ€” collecting column i from every row.

03

When i = 0: [row[0] for row in matrix] โ†’ [1, 5, 9] (first element of each row).

04

When i = 1: [row[1] for row in matrix] โ†’ [2, 6, 10] (second element of each row).

05

Each result is one row of the transposed matrix. The outer comprehension collects all four into a list of lists.

PythonKiddo example โ€” traced
matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]

# Trace what happens at each step of the outer loop:
# i = 0 โ†’ [row[0] for row in matrix] โ†’ [1,  5,  9]
# i = 1 โ†’ [row[1] for row in matrix] โ†’ [2,  6, 10]
# i = 2 โ†’ [row[2] for row in matrix] โ†’ [3,  7, 11]
# i = 3 โ†’ [row[3] for row in matrix] โ†’ [4,  8, 12]

# All four collected:
result = [[row[i] for row in matrix] for i in range(4)]
print(result)
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

zip() โ€” The Pythonic Shortcut

The docs make an important point here: for matrix transposition specifically, zip() is cleaner and faster. Understanding when to use a built-in instead of a comprehension is a key Python skill.

From the Python docs

In the real world, you should prefer built-in functions to complex flow statements. The zip() function would do a great job for this use case.

PythonPython docs example
matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]

# zip(*matrix) โ€” one line, same result
list(zip(*matrix))
# [(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

# Note: zip gives tuples, not lists.
# If you need lists, wrap with a list comprehension:
[list(row) for row in zip(*matrix)]
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

How the * (asterisk) works here

The * in zip(*matrix) is the unpacking operator. It expands the list of rows into separate arguments, as if you'd written zip([1,2,3,4], [5,6,7,8], [9,10,11,12]). zip() then pairs up the first elements, second elements, and so on โ€” which is exactly transposition.

PythonKiddo example
# These two are identical:
list(zip(*matrix))
list(zip([1,2,3,4], [5,6,7,8], [9,10,11,12]))

# zip() pairs up by position:
# (row0[0], row1[0], row2[0]) โ†’ (1, 5, 9)
# (row0[1], row1[1], row2[1]) โ†’ (2, 6, 10)
# ... and so on
When to use which: zip(*matrix) for straightforward transposition โ€” it's faster and more readable. Use a nested comprehension when you need to transform or filter the values as you transpose, not just rearrange them.

Real-World Uses of Nested List Comprehensions

Creating a multiplication table

PythonKiddo example
# 5x5 multiplication table
table = [[row * col for col in range(1, 6)] for row in range(1, 6)]

for row in table:
    print(row)
# [1,  2,  3,  4,  5]
# [2,  4,  6,  8, 10]
# [3,  6,  9, 12, 15]
# [4,  8, 12, 16, 20]
# [5, 10, 15, 20, 25]

Flattening a 2D list

PythonKiddo example
# Turn a list of lists into a flat list
nested = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in nested for num in row]
print(flat)   # [1, 2, 3, 4, 5, 6, 7, 8, 9]

# Note: this is a single comprehension with two for clauses,
# not a nested comprehension โ€” but it's closely related

Filtering a 2D grid

PythonKiddo example
# Keep only even numbers from each row
matrix = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
evens = [[x for x in row if x % 2 == 0] for row in matrix]
print(evens)
# [[2, 4], [6, 8], [10, 12]]

# Replace negatives with 0 across a grid
grid = [[-1, 2, -3], [4, -5, 6], [-7, 8, 9]]
cleaned = [[max(0, x) for x in row] for row in grid]
print(cleaned)
# [[0, 2, 0], [4, 0, 6], [0, 8, 9]]

Common Nested List Comprehension Mistakes

Getting the for clause order backwards

In [[row[i] for row in matrix] for i in range(4)], the outer loop is for i in range(4) (rightmost). Beginners often flip this. Read right to left: outer loop first, inner comprehension second.

Confusing nested comprehensions with two-for comprehensions

[[...] for x in a] is a nested comprehension โ€” creates a list of lists. [... for x in a for y in b] is a single comprehension with two for clauses โ€” creates a flat list. They look similar but produce different structures.

Using a nested comprehension when zip() is cleaner

For pure transposition, list(zip(*matrix)) is shorter, faster, and more readable than [[row[i] for row in matrix] for i in range(len(matrix[0]))]. Use zip() unless you need to transform values during transposition.

Forgetting zip() gives tuples, not lists

list(zip(*matrix)) produces a list of tuples: [(1, 5, 9), ...]. If you need lists of lists, wrap it: [list(row) for row in zip(*matrix)].

Writing deeply nested comprehensions that nobody can read

Two levels is fine. Three or more levels is almost always better written as named loops with comments. The Python docs themselves say to prefer built-in functions over complex flow statements โ€” the same logic applies to comprehensions.

Python Nested List Comprehension Interview Questions

Q1What is a nested list comprehension in Python?
A nested list comprehension is a list comprehension whose expression is itself another list comprehension. It creates a list of lists โ€” a 2D structure. From the Python docs: "The initial expression in a list comprehension can be any arbitrary expression, including another list comprehension." Most commonly used for building or transforming matrices and grids.
Q2How do you transpose a matrix using a nested list comprehension?
[[row[i] for row in matrix] for i in range(len(matrix[0]))]. The outer loop iterates over column indices. For each column index i, the inner comprehension picks the i-th element from every row โ€” which is the i-th column. The result is those columns assembled as rows.
Q3What does zip(*matrix) do and how does it relate to transposition?
The * unpacks the matrix into separate row arguments. zip() then pairs up elements by position โ€” the first element of each row, then the second, and so on. This is exactly matrix transposition. list(zip(*matrix)) returns tuples; wrap with [list(row) for row in zip(*matrix)] if you need lists.
Q4How do you flatten a 2D list in Python?
[item for row in matrix for item in row]. This is a single list comprehension with two for clauses โ€” not a nested comprehension. The outer loop iterates over rows, the inner loop over each item in that row. For deeply nested structures, itertools.chain.from_iterable() is more efficient.
Q5When should you use zip() instead of a nested comprehension?
The Python docs say it directly: prefer built-in functions over complex flow statements. Use zip() for pure transposition โ€” it's faster, shorter, and more readable. Use a nested comprehension when you need to filter or transform values as you build the transposed structure, not just rearrange them.

Python Nested List Comprehensions FAQ

A list comprehension whose expression is another list comprehension. It creates a list of lists. Syntax: [[inner_expr for item in inner_iter] for item in outer_iter]. From the Python docs ยง5.1.4: the inner comprehension is evaluated in the context of the outer for loop.
Read the outer loop first (the rightmost for), then the inner expression. In [[row[i] for row in matrix] for i in range(4)]: first i takes values 0,1,2,3. For each i, the inner comprehension [row[i] for row in matrix] runs completely and produces one row of the result.
Transposition flips a matrix โ€” rows become columns and columns become rows. Three ways in Python: nested list comprehension [[row[i] for row in matrix] for i in range(4)], or list(zip(*matrix)) (returns tuples), or [list(row) for row in zip(*matrix)] (returns lists). The zip() approach is preferred for pure transposition.
The * is the unpacking operator. It expands the list into separate positional arguments. So zip(*matrix) is equivalent to zip(matrix[0], matrix[1], matrix[2]) โ€” passing each row as a separate argument to zip(). See the Python docs link to Unpacking Argument Lists for more detail.
No โ€” they produce different structures. A nested comprehension [[x for x in row] for row in matrix] creates a list of lists (2D). A comprehension with two for clauses [x for row in matrix for x in row] creates a flat list (1D). Same data, different shape.
PM
Palak Mishra Backend developer ยท Building beginner-friendly Python docs at Kiddo
More Python topics โ†’

List Comprehension (5.1.3) ยท Lists (5.1) ยท Tuples (5.3) ยท Dictionaries (5.5) ยท Sets (5.4) ยท Lists as Stack and Queue ยท For Loop ยท If Statements ยท User Input ยท While Loop ยท