Module 2 ยท Lesson 8

List Comprehensions

๐Ÿ Pythonโฑ 12 min read๐Ÿ“– Pythonic

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

Practice Exercises

  1. Use a list comprehension to get all numbers divisible by 3 or 5 between 1 and 50.
  2. Given a list of strings, create a dictionary mapping each string to its reversed version.
  3. Use a set comprehension to find all unique first letters from a list of words.
  4. 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.
โ† Loops