Python List Comprehensions: Dict, Set and Generator Expressions

By Engr. Henry Akpan

One Line Instead of Four

A very common task in programming is to build a new list from an old one: square every number, keep only the passing scores, clean up a list of names. In Python you can do this with a for loop and append(), but there is a shorter, faster and more readable way: the list comprehension. This tutorial covers Python list comprehensions step by step: the basic syntax, filtering with if, transforming with if/else, nested loops, working with strings and files, dictionary and set comprehensions, generator expressions, and when not to use a comprehension. All examples were run on Python 3.13, and the outputs shown are real. It builds on the tutorials on Python lists and loops.

From Loop to Comprehension

Here is the traditional way to make a list of squares:

squares = []
for n in range(1, 6):
    squares.append(n * n)
print(squares)

Output:

[1, 4, 9, 16, 25]

The same result with a list comprehension:

squares = [n * n for n in range(1, 6)]
print(squares)

Output:

[1, 4, 9, 16, 25]

Read it as "n * n for each n in range(1, 6)". The general form is:

[expression for item in iterable]

The square brackets say "build a list". The expression is what goes into the new list, and the for part says where the items come from. Any iterable works: a list, a string, a range, a dictionary, a file or a tuple.

Transforming Items

The expression can be any Python expression, including method calls and function calls.

names = ["  ada ", "BAYO", "chidi  "]
clean = [name.strip().title() for name in names]
print(clean)

prices = [1200, 3500, 800]
with_vat = [round(p * 1.075) for p in prices]
print(with_vat)

lengths = [len(word) for word in "the quick brown fox".split()]
print(lengths)

Output:

['Ada', 'Bayo', 'Chidi']
[1290, 3762, 860]
[3, 5, 5, 3]

Filtering with if

Add an if clause at the end to keep only the items that pass a test:

[expression for item in iterable if condition]

scores = [72, 45, 88, 39, 50, 61]
passed = [s for s in scores if s >= 50]
print(passed)

evens = [n for n in range(20) if n % 2 == 0]
print(evens)

words = ["apple", "Banana", "cherry", "Avocado"]
a_words = [w for w in words if w.lower().startswith("a")]
print(a_words)

Output:

[72, 88, 50, 61]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
['apple', 'Avocado']

Choosing Between Two Values with if/else

To transform every item one of two ways (rather than dropping some), put a conditional expression in the expression part, before the for:

scores = [72, 45, 88, 39, 50, 61]
results = ["Pass" if s >= 50 else "Fail" for s in scores]
print(results)

numbers = [3, -1, 4, -5, 9]
no_negatives = [n if n > 0 else 0 for n in numbers]
print(no_negatives)

Output:

['Pass', 'Fail', 'Pass', 'Fail', 'Pass', 'Pass']
[3, 0, 4, 0, 9]

Remember the difference: an if at the end filters (the list can get shorter), while if/else at the start chooses a value (the list stays the same length). Writing [s for s in scores if s >= 50 else 0] is a syntax error.

Grading Example

Comprehensions combine nicely with functions. Here, a helper converts scores to grades on a typical Nigerian university scale:

def grade(score):
    if score >= 70: return "A"
    if score >= 60: return "B"
    if score >= 50: return "C"
    if score >= 45: return "D"
    return "F"

scores = [72, 45, 88, 39, 50, 61]
grades = [grade(s) for s in scores]
print(grades)
print("Distinctions:", sum(1 for g in grades if g == "A"))

Output:

['A', 'D', 'A', 'F', 'C', 'B']
Distinctions: 2

The last line uses a generator expression inside sum(), which is explained below.

Nested Loops in a Comprehension

A comprehension can contain more than one for. They run in the same order as nested loops written out in full: the first for is the outer loop.

pairs = [(x, y) for x in range(1, 4) for y in "ab"]
print(pairs)

# The same thing as ordinary loops:
pairs2 = []
for x in range(1, 4):
    for y in "ab":
        pairs2.append((x, y))
print(pairs == pairs2)

Output:

[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b')]
True

Flattening and building 2D lists

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [value for row in matrix for value in row]
print(flat)

transposed = [[row[i] for row in matrix] for i in range(3)]
print(transposed)

grid = [[0] * 3 for _ in range(2)]
grid[0][0] = 5
print(grid)

Output:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
[[5, 0, 0], [0, 0, 0]]

The last example is the safe way to build a grid. Writing [[0] * 3] * 2 instead would create two references to the same inner list, so changing one row would change both. The tutorial on mutability and copying explains why.

Dictionary and Set Comprehensions

The same idea works for dictionaries (a dictionary comprehension uses curly braces and key: value) and sets (curly braces, no colon):

students = ["Ada", "Bayo", "Chidi"]
scores = [72, 45, 88]

report = {name: score for name, score in zip(students, scores)}
print(report)

passed = {name: s for name, s in report.items() if s >= 50}
print(passed)

squares = {n: n ** 2 for n in range(1, 5)}
print(squares)

first_letters = {name[0] for name in ["Ada", "Amaka", "Bayo", "Bisi", "Chidi"]}
print(sorted(first_letters))

Output:

{'Ada': 72, 'Bayo': 45, 'Chidi': 88}
{'Ada': 72, 'Chidi': 88}
{1: 1, 2: 4, 3: 9, 4: 16}
['A', 'B', 'C']

Sets have no fixed order, so the example sorts the set before printing. A handy trick is swapping keys and values: {v: k for k, v in d.items()}.

Generator Expressions

Replace the square brackets with round brackets and you get a generator expression. It produces items one at a time, on demand, instead of building the whole list in memory. When you only need to loop over the results once, for example inside sum(), max(), any() or all(), a generator is more efficient.

total = sum(n * n for n in range(1, 1_000_001))
print(total)

scores = [72, 45, 88, 39]
print(any(s < 40 for s in scores))
print(all(s >= 40 for s in scores))

gen = (n * n for n in range(3))
print(next(gen), next(gen), next(gen))

Output:

333333833333500000
True
False
0 1 4

The first line adds a million squares without ever storing a million-item list. The tutorial on iterators and generators covers this lazy evaluation in depth.

Comprehensions with Files and Strings

Comprehensions are perfect for quick data processing. This example writes a small CSV file, then reads it back and extracts the students who passed:

with open("scores.csv", "w") as f:
    f.write("name,score\nAda,72\nBayo,45\nChidi,88\nDayo,39\n")

with open("scores.csv") as f:
    rows = [line.strip().split(",") for line in f][1:]

passed = [name for name, score in rows if int(score) >= 50]
print(rows)
print(passed)

vowels = [ch for ch in "comprehension" if ch in "aeiou"]
print("".join(vowels))

Output:

[['Ada', '72'], ['Bayo', '45'], ['Chidi', '88'], ['Dayo', '39']]
['Ada', 'Chidi']
oeeio

The [1:] slice skips the header row. For real CSV files with quoted commas, use the csv module, as shown in the tutorial on Python file handling.

When Not to Use Python List Comprehensions

  • When it becomes hard to read. More than two for clauses, or long conditions, are usually clearer as ordinary loops.
  • For side effects. Do not write [print(x) for x in items]. It builds a useless list of None values. Use a normal for loop to print, save or send things.
  • When you need to stop early. A comprehension cannot break. Use a loop, or next() with a generator expression to find the first match.
  • When the data is huge and you only loop once: prefer a generator expression to save memory.

Comprehensions are usually a little faster than the equivalent append() loop, because the looping happens in optimised C code inside Python, but readability should be the main reason to choose them.

Python List Comprehensions: Practice Exercises

  1. Make a list of the cubes of the numbers 1 to 10.
  2. From temps = [31, 28, 35, 22, 30], make a list of temperatures above 29.
  3. Convert a list of Celsius temperatures to Fahrenheit (F = C × 9/5 + 32).
  4. Make a dictionary mapping each word in a sentence to its length.
  5. Use a generator expression to count how many numbers from 1 to 100 are divisible by 7.

Here are model solutions:

print([n ** 3 for n in range(1, 11)])
temps = [31, 28, 35, 22, 30]
print([t for t in temps if t > 29])
print([c * 9 / 5 + 32 for c in [0, 25, 100]])
print({w: len(w) for w in "python makes lists easy".split()})
print(sum(1 for n in range(1, 101) if n % 7 == 0))

Output:

[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
[31, 35, 30]
[32.0, 77.0, 212.0]
{'python': 6, 'makes': 5, 'lists': 5, 'easy': 4}
14

Conclusion

Python list comprehensions, written as [expression for item in iterable if condition], build a new list in a single readable line. Put if at the end to filter, and if/else at the start to choose between values. Multiple for clauses act like nested loops, curly braces give dictionary and set comprehensions, and round brackets give memory-efficient generator expressions. Use comprehensions to build collections, and plain loops when the logic gets complicated or when you need side effects.

Related Tutorials