Lesson 96 min read

Comprehensions

Build entire collections in a single, elegant line

List Comprehensions — The Pythonic Shortcut

A list comprehension is like a factory assembly line: raw materials go in one end, get transformed, and finished products come out the other end — all in one smooth operation.

Instead of writing a loop, creating an empty list, and appending items one by one, you describe the entire thing in a single line: [expression for item in iterable].

The basic pattern is: [what_you_want for each_item in some_collection]

List Comprehensions vs. Loops

# The old way (loop)
squares_loop = []
for n in range(1, 6):
squares_loop.append(n ** 2)
print("Loop:", squares_loop)
# The comprehension way — same result, one line!
squares_comp = [n ** 2 for n in range(1, 6)]
print("Comp:", squares_comp)
# Transform strings
names = ["alice", "bob", "charlie"]
shouting = [name.upper() for name in names]
print(shouting)
# Extract data
prices = [{"item": "coffee", "price": 4.50},
{"item": "muffin", "price": 3.25},
{"item": "juice", "price": 5.00}]
item_names = [p["item"] for p in prices]
print(item_names)
Output
Loop: [1, 4, 9, 16, 25]
Comp: [1, 4, 9, 16, 25]
['ALICE', 'BOB', 'CHARLIE']
['coffee', 'muffin', 'juice']

Filtering with Conditions

You can add an if clause at the end to filter which items make it through the assembly line. Only items where the condition is True get included in the result.

Pattern: [expression for item in iterable if condition]

You can even use if/else in the expression part (before for) to transform items differently based on a condition.

Filtering & Conditional Expressions

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Filter: only even numbers
evens = [n for n in numbers if n % 2 == 0]
print("Evens:", evens)
# Filter: words longer than 4 characters
words = ["hi", "python", "is", "absolutely", "cool"]
long_words = [w for w in words if len(w) > 4]
print("Long words:", long_words)
# Conditional expression (if/else in the expression)
labels = ["even" if n % 2 == 0 else "odd" for n in range(1, 6)]
print("Labels:", labels)
# Real-world: clean and validate emails
raw_emails = [" [email protected] ", "", "[email protected]", " ", "[email protected]"]
clean = [e.strip().lower() for e in raw_emails if e.strip()]
print("Valid emails:", clean)
Output
Evens: [2, 4, 6, 8, 10]
Long words: ['python', 'absolutely']
Labels: ['odd', 'even', 'odd', 'even', 'odd']
Valid emails: ['[email protected]', '[email protected]', '[email protected]']

Dict & Set Comprehensions

The same idea works for dictionaries and sets! Just change the brackets:

  • Dict comprehension: {key: value for item in iterable} — uses curly braces with a colon.
  • Set comprehension: {expression for item in iterable} — curly braces, no colon.

Dict & Set Comprehensions

# Dict comprehension — word lengths
words = ["cat", "elephant", "dog", "hippopotamus"]
word_lengths = {w: len(w) for w in words}
print(word_lengths)
# Dict comprehension — flip keys and values
original = {"a": 1, "b": 2, "c": 3}
flipped = {v: k for k, v in original.items()}
print(flipped)
# Dict from two lists using zip()
countries = ["US", "UK", "JP"]
capitals = ["Washington", "London", "Tokyo"]
country_map = {c: cap for c, cap in zip(countries, capitals)}
print(country_map)
# Set comprehension — unique first letters
animals = ["ant", "bear", "cat", "ant", "butterfly", "cobra"]
first_letters = {a[0] for a in animals}
print(f"Unique first letters: {first_letters}")
Output
{'cat': 3, 'elephant': 8, 'dog': 3, 'hippopotamus': 12}
{1: 'a', 2: 'b', 3: 'c'}
{'US': 'Washington', 'UK': 'London', 'JP': 'Tokyo'}
Unique first letters: {'a', 'c', 'b'}

Generator Expressions — The Lazy Cousin

A generator expression looks like a list comprehension but uses () instead of []. It doesn't build the whole list in memory — it produces items one at a time, on demand. This is perfect for huge datasets where you'd run out of memory building a full list.

Generator Expressions

# Generator expression — lazy evaluation
gen = (n ** 2 for n in range(1, 6))
print(type(gen)) # It's a generator, not a list!
# Consume one at a time
print(next(gen)) # 1
print(next(gen)) # 4
# Use directly in functions — no extra brackets needed!
total = sum(n ** 2 for n in range(1, 11))
print(f"Sum of squares 1-10: {total}")
# Memory comparison
import sys
big_list = [n for n in range(10_000)]
big_gen = (n for n in range(10_000))
print(f"List size: {sys.getsizeof(big_list):,} bytes")
print(f"Generator size: {sys.getsizeof(big_gen)} bytes")
Output
<class 'generator'>
1
4
Sum of squares 1-10: 385
List size: 85,176 bytes
Generator size: 200 bytes
Note: Comprehensions are powerful, but don't go overboard. If your comprehension gets longer than about 80 characters or has multiple if/for clauses, switch to a regular loop. Readability beats cleverness every time.

Quick check

What does [x * 2 for x in [1, 2, 3]] produce?
FunctionsFile I/O