Timothy was refactoring some data processing code when he hit a comprehension he couldn't understand. "Margaret, someone wrote this comprehension and I can't figure out what it does. It's all on one line and my brain just... stops."
Margaret looked at the code:
result = [item for sublist in data for item in sublist if item > 0 if item % 2 == 0]
"Ah," Margaret said. "That's a comprehension that's crossed the line from clever to confusing. Let me show you what's actually happening here."
The Problem: Comprehensions Gone Wrong
"Comprehensions are powerful," Margaret explained, "but they can become write-only code. When you nest loops and stack filters, the execution order isn't what most people expect."
Timothy nodded. "I thought comprehensions were supposed to make code more readable. This is the opposite."
"Exactly. Let's start simple and build up to this monster. First, a basic comprehension:"
numbers = [1, 2, 3, 4, 5]
squares = [n * n for n in numbers]
Tree View:
numbers = [1, 2, 3, 4, 5]
squares =
Comprehension: n * n
For n in numbers
English View:
Set numbers to [1, 2, 3, 4, 5].
Set squares to:
List comprehension: n * n
For each n in numbers
"This is clear," Margaret said. "You're creating a new list by squaring each number. The structure is simple: for each n in numbers, compute n * n."
Adding Filters
"Now let's add a filter:"
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_squares = [n * n for n in numbers if n % 2 == 0]
Tree View:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_squares =
Comprehension: n * n
For n in numbers
If n % 2 == 0
English View:
Set numbers to [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].
Set even_squares to:
List comprehension: n * n
For each n in numbers
If n % 2 == 0
Timothy traced through it. "So it iterates through numbers, keeps only the even ones with the if filter, then squares them?"
"Right. The filter comes after the for clause. Look at the structure - the If is indented under the For, showing it filters which items get processed."
The result:
[4, 16, 36, 64, 100]
Multiple Filters
Margaret showed him something that surprised many developers:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = [n for n in numbers if n > 3 if n % 2 == 0]
Tree View:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result =
Comprehension: n
For n in numbers
If n > 3
If n % 2 == 0
English View:
Set numbers to [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].
Set result to:
List comprehension: n
For each n in numbers
If n > 3
If n % 2 == 0
"Wait," Timothy said. "Two if statements in a row? Does that mean OR or AND?"
"That's the confusion point. Look at the structure - both If clauses are at the same indentation level under the For. Multiple if clauses are combined with AND logic. This is equivalent to if n > 3 and n % 2 == 0. Both conditions must be true."
The result:
[4, 6, 8, 10]
"So you're filtering for numbers greater than 3, AND even? Why not just write if n > 3 and n % 2 == 0?"
"Exactly my point," Margaret said. "Multiple if clauses are legal, but they're harder to read than a single if with and. The structure hides the logic."
Nested Loops
"Now here's where it gets tricky," Margaret said. "Nested loops in comprehensions."
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [item for row in matrix for item in row]
Tree View:
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat =
Comprehension: item
For row in matrix
For item in row
English View:
Set matrix to [[1, 2, 3], [4, 5, 6], [7, 8, 9]].
Set flat to:
List comprehension: item
For each row in matrix
For each item in row
Timothy stared at it. "So... you iterate row in matrix, then item in row?"
"Yes. Look at the structure - both For clauses are at the same indentation level, showing they're sequential. The order in the comprehension matches the order you'd write the loops."
She showed him the equivalent loop:
flat = []
for row in matrix:
for item in row:
flat.append(item)
"See? The comprehension reads left-to-right in the same order as the nested loops. The outer loop comes first, the inner loop comes second."
The result:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Combining Nested Loops and Filters
"Now we can understand that monster comprehension from the beginning," Margaret said.
data = [[1, -2, 3], [4, -5, 6], [7, 8, -9]]
result = [item for sublist in data for item in sublist if item > 0 if item % 2 == 0]
Tree View:
data = [[1, -2, 3], [4, -5, 6], [7, 8, -9]]
result =
Comprehension: item
For sublist in data
For item in sublist
If item > 0
If item % 2 == 0
English View:
Set data to [[1, -2, 3], [4, -5, 6], [7, 8, -9]].
Set result to:
List comprehension: item
For each sublist in data
For each item in sublist
If item > 0
If item % 2 == 0
Margaret broke it down: "Look at the structure - the two For clauses are sequential, then the two If clauses are indented under them. This is equivalent to:"
result = []
for sublist in data: # First For
for item in sublist: # Second For
if item > 0: # First If
if item % 2 == 0: # Second If
result.append(item)
Timothy worked through it step by step:
[4, 6, 8]
"So it flattens the nested lists, keeps only positive numbers, then keeps only even numbers from those?"
"Exactly. But the comprehension version packs all that logic into one dense line. The structure is there, but it's hard to see without the tool breaking it down."
When Comprehensions Become Too Complex
"So when should I stop using comprehensions?" Timothy asked.
Margaret showed him the decision tree:
"Use comprehensions when:
- The logic fits comfortably on one line
- The transformation is simple and obvious
- You have at most one level of nesting
- Filters are straightforward
Avoid comprehensions when:
- You need nested loops with complex conditions
- The logic requires multiple steps
- Reading it aloud doesn't make immediate sense
- You're tempted to add comments explaining it"
She rewrote the complex comprehension more clearly:
# More readable version
result = []
for sublist in data:
for item in sublist:
if item > 0 and item % 2 == 0:
result.append(item)
"Or even better, break it into steps:"
# Most readable version
flat = [item for sublist in data for item in sublist]
positive = [item for item in flat if item > 0]
even = [item for item in positive if item % 2 == 0]
Timothy looked relieved. "So it's okay to use multiple simple comprehensions instead of one complex one?"
"Absolutely. Readability beats cleverness every time."
Conditional Expressions in Comprehensions
"There's one more pattern that confuses people," Margaret said. "The conditional expression."
numbers = [1, 2, 3, 4, 5]
result = [n if n % 2 == 0 else -n for n in numbers]
Tree View:
numbers = [1, 2, 3, 4, 5]
result =
Comprehension: n if n % 2 == 0 else -n
For n in numbers
English View:
Set numbers to [1, 2, 3, 4, 5].
Set result to:
List comprehension: n if n % 2 == 0 else -n
For each n in numbers
"This is different from a filter," Margaret explained. "Look at the structure - the if/else is part of the comprehension expression itself, not a separate filter clause. It transforms every item - even numbers stay positive, odd numbers become negative."
The result:
[-1, 2, -3, 4, -5]
"Compare that to a filter:"
result = [n for n in numbers if n % 2 == 0] # Only keeps even numbers
"The filter version only keeps even numbers. The conditional expression version processes all numbers but transforms them differently."
Timothy nodded. "So if/else in the expression transforms items, if as a separate clause filters them?"
"Exactly. The position and structure matter."
Dict and Set Comprehensions
"Comprehensions aren't just for lists," Margaret added.
# Dict comprehension
squares_dict = {n: n * n for n in range(1, 6)}
# Set comprehension
unique_lengths = {len(word) for word in ['cat', 'dog', 'bird', 'cat']}
Tree View:
squares_dict =
Comprehension: n: n * n
For n in range(1, 6)
unique_lengths =
Comprehension: len(word)
For word in ['cat', 'dog', 'bird', 'cat']
English View:
Set squares_dict to:
Dict comprehension: n: n * n
For each n in range(1, 6)
Set unique_lengths to:
Set comprehension: len(word)
For each word in ['cat', 'dog', 'bird', 'cat']
"Same structure, different types. The English View tells you whether it's a List, Dict, or Set comprehension based on the syntax."
Results:
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
{3, 4}
The Readability Test
Margaret pulled the lesson together. "The structure of comprehensions is elegant, but that elegance can hide complexity. Before you write a comprehension, ask yourself: can someone else read this and immediately understand what it does?"
Timothy looked at his original confusing comprehension. "So the rule is: if I need to use the Structure Viewer to understand it, it's too complex?"
"That's a good rule. Comprehensions should make code more readable, not less. The moment you sacrifice clarity for brevity, you've gone too far."
"So I should favor simple comprehensions and break complex ones into steps?"
"Exactly. The structure shows you what's happening, but if the structure requires multiple nested levels to parse, split it up. Your future self will thank you."
Analyze Python structure yourself: Download the Python Structure Viewer - a free tool that shows code structure in tree and plain English views. Works offline, no installation required.
Aaron Rose is a software engineer and technology writer at tech-reader.blog and the author of Think Like a Genius.
Top comments (0)