Python's Most Elegant Feature
List comprehensions let you create lists in a single, readable line. They replace multi-line loops with concise expressions that often read like English. They're one of Python's most distinctively Pythonic features.
# Traditional loop
squares = []
for x in range(10):
squares.append(x ** 2)
# squares = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# List comprehension โ same result
squares = [x ** 2 for x in range(10)]
# Read as: "give me x-squared for each x in range(10)"
# Syntax: [expression for item in iterable]With Filtering
# Syntax: [expression for item in iterable if condition]
# Only even squares
even_squares = [x ** 2 for x in range(10) if x % 2 == 0]
# [0, 4, 16, 36, 64]
# Filter words by length
words = ["hello", "hi", "hey", "goodbye", "bye"]
long_words = [w for w in words if len(w) > 3]
# ['hello', 'goodbye']
# Transform AND filter in one line
upper_long = [w.upper() for w in words if len(w) > 3]
# ['HELLO', 'GOODBYE']
# Multiple conditions
numbers = range(100)
fizzbuzz = [n for n in numbers if n % 3 == 0 and n % 5 == 0]
# Multiples of both 3 and 5: [0, 15, 30, 45, 60, 75, 90]Real-World Examples
# Extract email domains
emails = ["alice@gmail.com", "bob@yahoo.com", "carol@gmail.com"]
domains = [e.split("@")[1] for e in emails]
# ['gmail.com', 'yahoo.com', 'gmail.com']
# Parse CSV-like row
row = "1,2,3,4,5"
numbers = [int(x) for x in row.split(",")]
# [1, 2, 3, 4, 5]
# Normalize user inputs (strip whitespace, lowercase)
inputs = [" Alice ", " BOB", "Carol "]
clean = [s.strip().lower() for s in inputs]
# ['alice', 'bob', 'carol']
# Generate coordinate pairs
coords = [(x, y) for x in range(3) for y in range(3)]
# [(0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2)]
# Flatten a nested list (nested comprehension)
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Remove None values
data = [1, None, 3, None, 5]
clean = [x for x in data if x is not None]
# [1, 3, 5]Dict and Set Comprehensions
# Dictionary comprehension: {key: value for item in iterable}
names = ["alice", "bob", "carol"]
name_lengths = {name: len(name) for name in names}
# {'alice': 5, 'bob': 3, 'carol': 5}
# Invert a dictionary (swap keys and values)
d = {"a": 1, "b": 2, "c": 3}
inverted = {v: k for k, v in d.items()}
# {1: 'a', 2: 'b', 3: 'c'}
# Filter dict entries
scores = {"Alice": 95, "Bob": 60, "Carol": 82, "Dave": 55}
passing = {name: score for name, score in scores.items() if score >= 70}
# {'Alice': 95, 'Carol': 82}
# Set comprehension: {expression for item in iterable}
# Sets have no duplicates
unique_lengths = {len(w) for w in ["hello", "world", "hi", "hey"]}
# {2, 3, 5}
# Unique domains from emails
emails = ["a@gmail.com", "b@yahoo.com", "c@gmail.com"]
unique_domains = {e.split("@")[1] for e in emails}
# {'gmail.com', 'yahoo.com'}Generator Expressions
Like list comprehensions but lazy โ they compute values on demand. Use () instead of []. Memory-efficient for large datasets.
# List comprehension โ computes ALL values immediately
squares_list = [x**2 for x in range(1_000_000)] # ~8MB in memory
# Generator expression โ computes ONE value at a time
squares_gen = (x**2 for x in range(1_000_000)) # Tiny memory
# Use generators directly in functions
total = sum(x**2 for x in range(10)) # 285
largest = max(x**2 for x in range(10)) # 81
all_even = all(x % 2 == 0 for x in [2, 4, 6]) # True
any_negative = any(x < 0 for x in [1, -2, 3]) # True
# Use list() to materialize a generator
materialized = list(x**2 for x in range(5))
# [0, 1, 4, 9, 16]Generators are Single-Use
You can only iterate a generator once. After that, it's exhausted. If you need to iterate multiple times, use a list comprehension instead.
When NOT to Use Comprehensions
# GOOD โ readable, single operation
evens = [x for x in range(20) if x % 2 == 0]
# BAD โ too complex, use a regular loop
result = [x*y for x in range(5) for y in range(5) if x != y if x + y > 3]
# BETTER as a loop with clear logic
result = []
for x in range(5):
for y in range(5):
if x != y and x + y > 3:
result.append(x * y)
# BAD โ comprehension with side effects
[print(x) for x in range(10)] # Works but poor style
# GOOD โ use a loop for side effects
for x in range(10):
print(x)Readability Rule
Comprehensions should improve readability. If someone needs more than 5 seconds to understand it, use a loop with a comment instead. Clever isn't always good.
Key Takeaways
- List comp syntax:
[expr for item in iterable if condition] - Dict comp:
{k: v for k, v in pairs} - Set comp:
{expr for item in iterable}โ automatic deduplication - Generator expressions: use
()instead of[]for lazy, memory-efficient iteration - Readability first: if it's hard to read, use a loop
Practice Exercises
- Use a list comprehension to get all numbers divisible by 3 or 5 between 1 and 50.
- Given a list of strings, create a dictionary mapping each string to its reversed version.
- Use a set comprehension to find all unique first letters from a list of words.
- Use a generator expression with
sum()to calculate the sum of squares of all odd numbers from 1 to 99. Compare memory usage with a list comprehension version.