Python Nested List Comprehension: Syntax and Examples
python nested list comprehension: Understand Python nested list comprehension syntax, learn to flatten nested lists, apply conditions at any level, and know when a reg...
Python nested list comprehension syntax extends the standard comprehension form by adding extra for clauses, letting you flatten nested data, generate combinations, or transform multi-level structures in a single expression. The pattern is compact, but the reading order and the placement of conditions are easy to get wrong. Understanding how the clauses map to nested loops makes the behavior predictable, and knowing when the single-line form hurts readability keeps the code maintainable.
How Nested List Comprehension Syntax Works
A standard list comprehension has one for clause:
squares = [x * x for x in range(10)]
A nested version adds a second for clause:
pairs = [(x, y) for x in range(3) for y in range(3)]
The first for clause is the outer loop; the second is the inner loop. The expression at the front is evaluated once for every combination of x and y, producing nine pairs. The result is a flat list, not a list of lists.
Flattening a Nested List
The most common use of a nested list comprehension is flattening a two-dimensional structure:
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] flat = [value for row in matrix for value in row]
flat becomes [1, 2, 3, 4, 5, 6, 7, 8, 9]. The order of the for clauses is critical. for row in matrix runs first, and for value in row runs once per row. Swapping the clauses would raise a NameError because row would not be defined when the inner clause tries to iterate over it.
Adding Conditions to Inner and Outer Loops
An if condition can follow any for clause and applies to the loop immediately before it. To keep only even values:
even_values = [value for row in matrix for value in row if value % 2 == 0]
To skip short rows before iterating over their contents:
wide_rows = [value for row in matrix if len(row) > 2 for value in row]
The condition if len(row) > 2 filters rows first, so the inner loop never runs for rows that fail the check. This mirrors the behavior of nested for loops with an if statement in the outer body.
Reading Order: Loops Read Left to Right
A nested comprehension is equivalent to a set of nested for loops written in the same order. The comprehension:
result = [value for row in matrix for value in row if value % 2 == 0]
is equivalent to:
result = [] for row in matrix: for value in row: if value % 2 == 0: result.append(value)
Keeping this mapping in mind makes it easier to write a comprehension correctly the first time: write the loops in the order you would nest them, then move the innermost expression to the front.
Readability and Maintainability Tradeoffs
Nested comprehensions become hard to read once they exceed two levels of nesting. A three-level flatten:
flat = [item for group in data for row in group for item in row]
is difficult to parse at a glance, especially when conditions are mixed in. If the logic grows beyond two loops, or if the expression at the front is anything more than a simple variable reference, a regular loop with a descriptive name is usually easier to maintain. Comprehensions also should not produce side effects; if the loop needs to print, mutate an external structure, or log, write it as an explicit loop.
Performance Considerations
List comprehensions are generally faster than equivalent for loops that call append() because the iteration and appending run in optimized C code and avoid repeated attribute lookups. The tradeoff is memory: a nested comprehension builds the entire result list in memory. For very large inputs, a generator expression with the same nesting produces values lazily:
flat_gen = (value for row in matrix for value in row)
This avoids allocating the full list at once, at the cost of a small per-item overhead when iterating. If the result is consumed once, the generator is often the better choice; if the result is reused or indexed, materialize it as a list.
When to Use a Regular Loop Instead
A regular loop is the better choice when the nesting exceeds two levels, the logic requires break or continue, the loop has side effects, or the comprehension becomes harder to read than the loop it replaces. A comprehension that spans multiple lines and carries three conditions is no longer a readability win. The decision should be based on whether the single-expression form communicates the transformation clearly, not on whether it is technically possible to express it in one line.