List comprehensions are compact. In theory, this makes code cleaner. In practice, it means you can pack 5 different bugs into a single line of code.
When a standard for loop crashes, the traceback points to the exact line. When a comprehension crashes, you often just get a SyntaxError or a ValueError on the whole block, leaving you guessing.
Here are the 7 most common mistakes developers make, sorted by frequency.
Table of Contents
- The Syntax Trap (Where does 'If' go?)
- The Nested Loop Order Bug
- The "Leaking Variable" Myth
- The Mutable Default Trap
- Memory Errors (The Billion Item List)
- The "Tuple" Generator Bug
- Unreadable One-Liners
- FAQ
- Test Your Knowledge (Quiz)
1. The Syntax Trap (Where does 'If' go?)
This is responsible for 90% of SyntaxError: invalid syntax crashes.
The Mistake:
You want to transform items, but you put the if at the end with an else.
# WRONG
[x for x in items if x > 0 else 0]
The Fix: Remember the Golden Rule:
- Filtering (
ifwithoutelse) goes at the END. - Mapping (
ifwithelse) goes at the START.
# RIGHT (Mapping)
[x if x > 0 else 0 for x in items]
2. The Nested Loop Order Bug
When flattening a list, your intuition fails you.
The Mistake: You try to access the inner item before defining the outer loop.
# WRONG: "For item in sublist... wait, what is sublist?"
[item for item in sublist for sublist in matrix]
# NameError: name 'sublist' is not defined
The Fix: Read it Left-to-Right. Define the Outer loop first.
# RIGHT
[item for sublist in matrix for item in sublist]
# ^^^^ Outer ^^^^ ^^^^ Inner ^^^^
3. The "Leaking Variable" Myth
This isn't a bug in modern Python, but it scares people coming from Python 2.
The Myth:
x = 100
[x for x in range(5)]
print(x) # Will this be 100 or 4?
- Python 2: It would be
4. The variablexleaked! - Python 3: It is
100. The variablexis local to the comprehension.
The Fix: Stop worrying about this. Unless you are maintaining legacy 2.7 code, scope leaking is fixed.
4. The Mutable Default Trap
This is subtle and dangerous. Beware of calling functions with mutable defaults inside a comprehension.
The Mistake:
def add_item(item, list_buffer=[]):
list_buffer.append(item)
return list_buffer
# Creating a list of lists
res = [add_item(i) for i in range(3)]
Expected: [[0], [1], [2]]
Actual: [[0, 1, 2], [0, 1, 2], [0, 1, 2]]
Why? The default argument list_buffer is created once when the function is defined. Every iteration appends to the same list.
The Fix: Never use mutable defaults.
def add_item(item, list_buffer=None):
if list_buffer is None: list_buffer = []
...
5. Memory Errors (The Billion Item List)
The Mistake: Trying to process a massive file using a list comprehension.
# WRONG: Reads 50GB file into RAM
lines = [line for line in open("big_data.txt")]
The Fix: Use a Generator Expression. It lazily loads one line at a time.
# RIGHT: Uses 0 RAM
lines = (line for line in open("big_data.txt"))
6. The "Tuple" Generator Bug
The Mistake:
You think changing [] to () creates a Tuple.
data = (x * 2 for x in range(3))
print(data)
# <generator object ...>
# It's not a tuple!
The Fix: You must explicitly cast it.
data = tuple(x * 2 for x in range(3))
# (0, 2, 4)
7. Unreadable One-Liners
This is the most common "bug" of all: Code that works, but no one can read.
The Mistake:
[x for x in data if check(x) for y in x if check(y) else 0]
The Fix:
If it takes you 10 seconds to understand, rewrite it as a standard for loop. Your future self will thank you.
FAQ
Q1: Can I use try-except inside a comprehension?
A: No. Expressions cannot contain statements. You must write a wrapper function:
def safe_div(x):
try: return 100/x
except: return 0
[safe_div(x) for x in nums]
Q2: How do I debug a crashing comprehension?
A: You can't put a breakpoint inside the middle of a line easily. The strict answer is: Unroll it. Turn it into a loops, debug it, fix it, then roll it back up (or leave it unrolled).
Test Your Knowledge
Conclusion
List comprehensions are fragile constructs. One wrong colon or misplaced if breaks the whole thing. But now you know the signs, so you can fix them purely by looking at the specific error pattern.
Final Verification: always verify your logic on a small dataset before running it on a million items.
- Next Steps: Mastering Python Next Steps