085. List Comprehensions
Build lists concisely with expressions instead of append loops
085. List Comprehensions
He has a list of Process objects. He wants three derived lists: RSS in MB, only the heavy processes, and RSS scaled to GB.
His first attempt: three separate loops with append.
A senior dev shows him list comprehensions:
processes = [
{"name": "chrome", "rss_mb": 620},
{"name": "python", "rss_mb": 85},
{"name": "slack", "rss_mb": 310},
{"name": "vim", "rss_mb": 12},
]
# Extract: all RSS values
rss_values = [p["rss_mb"] for p in processes]
# β [620, 85, 310, 12]
# Filter: only heavy processes (> 200 MB)
heavy = [p for p in processes if p["rss_mb"] > 200]
# β [{chrome, 620}, {slack, 310}]
# Transform + filter: GB values for heavy processes
heavy_gb = [p["rss_mb"] / 1024 for p in processes if p["rss_mb"] > 200]
# β [0.605..., 0.302...]Three operations, three one-liners. Readable, no intermediate state.
π‘ Fun fact: List comprehensions in Python were inspired by set-builder notation in mathematics and by the list comprehensions in Haskell. They were added in Python 2.0 (2000) and were so well-received that Python later added dict comprehensions (Python 2.7/3.0), set comprehensions, and generator expressions using the same syntax pattern.
β οΈ Watch out: The most common mistake is putting the if condition in the wrong place. [f(x) if cond else g(x) for x in items] is a conditional expression that transforms every element. [f(x) for x in items if cond] is a filter that skips elements. They look similar but do completely different things.
π€ Think about it: [x * 2 for x in range(1_000_000)] builds a list of one million integers in memory immediately. (x * 2 for x in range(1_000_000)) is a generator that produces values one at a time. When would the generator version be significantly better, and are there cases where youβd prefer the list despite the memory cost?
Learning objectives
- Write list comprehensions as an alternative to append loops
- Add filter conditions to list comprehensions
- Apply transformations to each element
Key concepts
- list comprehension
- filter condition
- expression
- Pythonic style
Try it
Concept detail
List comprehension syntax: [expression for variable in iterable] [expression for variable in iterable if condition]
Examples: [ii for i in range(1, 6)] β [1, 4, 9, 16, 25] [x for x in nums if x % 2 == 0] β even numbers only [c9/5+32 for c in temps] β Celsius β Fahrenheit [p[βnameβ] for p in processes] β extract a field
Reading it: βgive me [expression] for each [variable] in [iterable] where [condition]β
Compared to append loop: # Loop: result = [] for x in items: if x > 0: result.append(x * 2)
# Comprehension β same thing, one line:
result = [x * 2 for x in items if x > 0]Nested comprehension (flatten a 2D list): [cell for row in matrix for cell in row]
Generator expression (lazy β no list in memory): (x*2 for x in items) # use when you only need to iterate once
When to use comprehensions:
- Single expression per element: ideal
- Simple filter condition: ideal
- Multiple lines of logic per element: use a regular loop for clarity
Solution
def squares(n):
return [i * i for i in range(1, n + 1)]
def even_numbers(numbers):
return [x for x in numbers if x % 2 == 0]
def celsius_to_fahrenheit(temps):
return [c * 9 / 5 + 32 for c in temps]Tests
def test_squares():
assert squares(5) == [1, 4, 9, 16, 25]
def test_squares_one():
assert squares(1) == [1]
def test_squares_empty():
assert squares(0) == []
def test_even_numbers():
assert even_numbers([1, 2, 3, 4, 5, 6]) == [2, 4, 6]
def test_even_empty():
assert even_numbers([1, 3, 5]) == []
def test_even_negatives():
assert even_numbers([-4, -3, -2, -1, 0]) == [-4, -2, 0]
def test_celsius_freezing():
result = celsius_to_fahrenheit([0, 100])
assert abs(result[0] - 32.0) < 0.001
assert abs(result[1] - 212.0) < 0.001
def test_celsius_body():
result = celsius_to_fahrenheit([37])
assert abs(result[0] - 98.6) < 0.1