Skip to main content

List and Dictionary Comprehensions

A comprehension creates a new collection by transforming and optionally filtering an iterable. It does not modify the source iterable.

List comprehensions​

The expression comes first, followed by the for clause and then any filter.

numbers = range(1, 6)
squares = [number**2 for number in numbers]

print(squares) # [1, 4, 9, 16, 25]

Add an if clause to keep selected inputs:

numbers = range(1, 11)
even_squares = [number**2 for number in numbers if number % 2 == 0]

print(even_squares) # [4, 16, 36, 64, 100]

Multiple for clauses follow the same order as equivalent nested loops. This common pattern flattens one level of nesting:

matrix = [[1, 2, 3], [4, 5, 6]]
flattened = [value for row in matrix for value in row]

print(flattened) # [1, 2, 3, 4, 5, 6]

Dictionary comprehensions​

A dictionary comprehension supplies both a key and a value expression.

words = ["data", "science", "python"]
lengths = {word: len(word) for word in words}

print(lengths) # {'data': 4, 'science': 7, 'python': 6}

If multiple inputs produce the same key, the later value replaces the earlier one, just as with ordinary dictionary assignment.

pairs = [("A", 1), ("B", 2), ("A", 3)]
latest = {key: value for key, value in pairs}

print(latest) # {'A': 3, 'B': 2}

Readability first​

Comprehensions are best for a single clear transformation with a small filter. Use an ordinary loop when the work has side effects, needs several branches, or would require a deeply nested expression.

readings = [3.5, None, 4.0, -1.0]
valid_readings = [value for value in readings if value is not None and value >= 0]

print(valid_readings) # [3.5, 4.0]

Generator expressions use parentheses and produce values lazily. They are useful when a consuming function does not need a list:

total = sum(number**2 for number in range(5))
print(total) # 30