Python Course
List Comprehensions
One of the most beloved features of Python is the ability to build lists in a single, readable line using list comprehensions. Instead of writing a full for loop to collect values into a list, you express the entire operation in one compact expression. Python programmers use list comprehensions constantly — they are faster to write, faster to read, and often faster to run than the equivalent loop.
This lesson starts with the basics and works up to filtering, nested comprehensions, and when not to use them — giving you the full picture professionals use every day.
The Problem List Comprehensions Solve
Consider the task of taking a list of numbers and building a new list where every value is doubled. Without list comprehensions, the standard approach uses a loop and an empty list:
# Traditional loop approach — building a list step by step
nums = [1, 2, 3, 4, 5] # original list
doubled = [] # start with empty list
for n in nums:
doubled.append(n * 2) # add each doubled value
print(doubled)This works perfectly — but it takes four lines to express one simple idea. List comprehensions collapse this into one clean line without sacrificing readability.
Basic List Comprehension Syntax
The syntax follows a natural English pattern: "give me [expression] for each [item] in [iterable]".
Why it exists: Python was designed to be expressive. List comprehensions let you write what you mean directly, without boilerplate code that exists only to satisfy the loop structure.
Real-world use: a data pipeline extracts every product price from a raw list of records in one line rather than looping through and appending to a collector list.
# Basic list comprehension — same result as the loop above
nums = [1, 2, 3, 4, 5] # original list
doubled = [n * 2 for n in nums] # expression | for | iterable
print(doubled)
# Another example — convert strings to uppercase
words = ["hello", "world", "python"]
upper = [w.upper() for w in words]
print(upper)['HELLO', 'WORLD', 'PYTHON']
- Structure:
[expression for item in iterable] - The expression is evaluated once for every item in the iterable
- The result is always a new list — the original is never modified
- Any iterable works: lists, tuples, strings, ranges, and more
Using range() in a List Comprehension
range() pairs naturally with list comprehensions to generate number sequences without a pre-existing list.
# Generating lists from range()
squares = [x ** 2 for x in range(1, 6)] # squares of 1 through 5
print("Squares:", squares)
even = [x for x in range(0, 20, 2)] # every even number 0–18
print("Evens:", even)
cents = [round(x * 0.01, 2) for x in range(1, 6)] # $0.01 to $0.05
print("Cents:", cents)Evens: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
Cents: [0.01, 0.02, 0.03, 0.04, 0.05]
range()is lazy — it generates values one at a time, which is memory-efficient- Combine with any arithmetic expression to produce custom number sequences
- This replaces the common pattern of
result = []; for i in range(...): result.append(...)
Filtering with a Condition (if clause)
You can add an if clause at the end of a list comprehension to include only items that pass a test. This replaces the common loop-plus-if pattern in a single line.
Why it exists: filtering and transforming data are two of the most common operations in any program. Combining them in one readable expression is a major productivity win.
Real-world use: a web app filters a list of user accounts to return only those marked as active, in one line rather than a loop with an if-check and append.
# List comprehension with an if filter
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Keep only even numbers
evens = [n for n in nums if n % 2 == 0]
print("Evens:", evens)
# Keep only numbers greater than 5
big = [n for n in nums if n > 5]
print("Greater than 5:", big)
# Filter a list of prices — keep items under $20
prices = [5.99, 24.99, 12.50, 49.99, 8.00]
budget = [p for p in prices if p < 20]
print("Under $20:", budget)Greater than 5: [6, 7, 8, 9, 10]
Under $20: [5.99, 12.5, 8.0]
- Structure with filter:
[expression for item in iterable if condition] - Items where the condition is
Falseare skipped — not included in the result - The
ifclause tests the original item, while the expression can transform it - You can use any valid boolean expression as the filter
Transforming and Filtering Together
The real power comes from combining a transformation expression with a filter condition in the same comprehension — keep only certain items and change them at the same time.
# Transform AND filter in one comprehension
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Square only the even numbers
even_squares = [n ** 2 for n in nums if n % 2 == 0]
print("Even squares:", even_squares)
# Strip whitespace and keep only non-empty strings
raw = [" apple ", "", " ", "banana", " cherry "]
clean = [s.strip() for s in raw if s.strip()]
print("Cleaned:", clean)Cleaned: ['apple', 'banana', 'cherry']
- The expression (left side) transforms — the condition (right side) filters
- Python evaluates the condition first, then applies the expression to passing items
- This single line replaces a loop with an if-check, a transformation, and an append
if/else Inside a List Comprehension
You can also use an inline if/else (ternary expression) directly inside the expression part to choose between two output values for every item. Note the position: the ternary goes before for, not after it.
# Ternary if/else in the expression part
nums = [1, 2, 3, 4, 5, 6]
# Label each number as "even" or "odd"
labels = ["even" if n % 2 == 0 else "odd" for n in nums]
print(labels)
# Replace negatives with 0, keep positives as-is
data = [-3, 7, -1, 4, 0, -9, 2]
clipped = [n if n > 0 else 0 for n in data]
print(clipped)[0, 7, 0, 4, 0, 0, 2]
- Structure:
[val_if_true if condition else val_if_false for item in iterable] - Every item is included — the ternary chooses which value to assign, not whether to include
- This is different from a trailing
if, which filters items out entirely - Keep the expression simple — if the logic is complex, a regular loop is clearer
Nested List Comprehensions
You can nest one list comprehension inside another to work with two-dimensional data structures like grids, matrices, or lists of lists.
Why it exists: flattening or transforming 2D data is a common task in data processing. Nested comprehensions express this without double-nested loops and append calls.
Real-world use: a data science pipeline flattens a matrix of weekly sales figures into a single list for aggregate calculations.
# Nested list comprehension — flattening a 2D list
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
# Flatten: pull every item from every inner list into one flat list
flat = [val for row in matrix for val in row]
print("Flat:", flat)
# Build a multiplication table as a 2D list
table = [[r * c for c in range(1, 4)] for r in range(1, 4)]
for row in table:
print(row)[1, 2, 3]
[2, 4, 6]
[3, 6, 9]
- Flatten structure:
[expr for outer in list for inner in outer]— reads left to right like nested loops - 2D build structure:
[[expr for inner in range] for outer in range]— outer loop last, result is a list of lists - Beyond two levels of nesting, a regular loop is usually clearer
List Comprehension vs map() and filter()
Before list comprehensions existed, Python programmers used map() and filter() with lambda functions for the same tasks. List comprehensions are now the preferred Pythonic style for most situations.
# Comparing list comprehension to map() and filter()
nums = [1, 2, 3, 4, 5]
# map() — apply a function to every item
doubled_map = list(map(lambda n: n * 2, nums))
# list comprehension equivalent
doubled_comp = [n * 2 for n in nums]
print(doubled_map) # [2, 4, 6, 8, 10]
print(doubled_comp) # [2, 4, 6, 8, 10]
# filter() — keep items that pass a test
evens_filter = list(filter(lambda n: n % 2 == 0, nums))
evens_comp = [n for n in nums if n % 2 == 0]
print(evens_filter) # [2, 4]
print(evens_comp) # [2, 4][2, 4, 6, 8, 10]
[2, 4]
[2, 4]
- List comprehensions are considered more readable than
map()/filter()+ lambda in most cases map()andfilter()return lazy iterators — wrap withlist()to get a list- For simple one-argument functions,
map()with a named function can still be elegant - PEP 8 (Python's style guide) generally recommends list comprehensions over
map/filterwith lambdas
When Not to Use List Comprehensions
List comprehensions are powerful but not always the right tool. Knowing when to use a regular loop instead is a sign of mature Python judgment.
- Side effects: if the loop body exists to call a function for its effect (e.g.,
print(), writing to a file), use a regular loop. A comprehension that discards its result is misleading. - Complex logic: if the expression or condition requires multiple lines or nested ternaries, a loop with clear variable names is more readable and maintainable.
- Very large data: a list comprehension always builds the entire list in memory at once. For huge datasets, a generator expression (covered in Lesson 26) is more memory-efficient.
- Debugging: it is harder to add a breakpoint or print statement inside a comprehension. During debugging, a loop lets you inspect values at each step.
# When a regular loop is clearer than a comprehension
items = ["apple", "banana", "cherry"]
# BAD — comprehension used only for side effects (result thrown away)
# [print(item) for item in items] # don't do this
# GOOD — use a loop when the purpose is side effects
for item in items:
print(item) # clear intent: we are printing, not building a listbanana
cherry
Summary Table
| Pattern | Syntax | What It Does |
|---|---|---|
| Basic | [expr for x in iterable] |
Transform every item |
| Filter | [expr for x in iterable if cond] |
Transform only matching items |
| Ternary | [a if cond else b for x in iterable] |
Choose between two values per item |
| Flatten | [expr for row in list for x in row] |
Collapse a 2D list into 1D |
| 2D Build | [[expr for x in r] for y in r] |
Build a matrix / list of lists |
| From range | [expr for x in range(n)] |
Generate a numeric sequence |
Practice Questions
Practice 1. What is the list comprehension syntax to create a list of squares for numbers 1 through 5? Write only the comprehension expression.
Practice 2. In a list comprehension, where does the if filter condition go?
Practice 3. What type of object does a list comprehension always produce?
Practice 4. What is the key difference between a trailing if filter and an inline if/else ternary in a list comprehension?
Practice 5. Name one situation where a regular for loop is preferred over a list comprehension.
Quiz
Quiz 1. What is the output of [x * 2 for x in range(1, 4)]?
Quiz 2. Which of the following correctly filters a list to keep only values greater than 3?
Quiz 3. What does ["yes" if x % 2 == 0 else "no" for x in range(1, 4)] produce?
Quiz 4. What does [val for row in [[1,2],[3,4]] for val in row] produce?
Quiz 5. Why is a generator expression preferred over a list comprehension for very large datasets?