Skip to main content

Comprehensions

Comprehensions provide a concise, readable way to create lists, dictionaries, and sets by transforming or filtering data. They're a Pythonic alternative to loops when you're building collections, making your code more compact and often faster.

What are comprehensions?

A comprehension is a compact way to create collections (lists, dictionaries, or sets) from iterables. Instead of writing a loop with append() or similar methods, comprehensions let you express the transformation in a single line.

List comprehensions

List comprehensions create lists by transforming or filtering items from an iterable.

Basic syntax

[<expression> for <item> in <iterable>]

Simple example

# Create a list of squares
squares = [x**2 for x in range(5)] # See [ranges](./ranges) for more on range()
print(squares) # [0, 1, 4, 9, 16]

This is equivalent to:

squares = []
for x in range(5):
squares.append(x**2)

With conditions (filtering)

Add an if clause to filter items:

# Only even numbers
evens = [x for x in range(10) if x % 2 == 0] # See [ranges](./ranges) for more on range()
print(evens) # [0, 2, 4, 6, 8]

This is equivalent to:

evens = []
for x in range(10):
if x % 2 == 0:
evens.append(x)

Transforming items

Apply operations to each item:

# Convert strings to uppercase
words = ['hello', 'world', 'python']
uppercase = [word.upper() for word in words]
print(uppercase) # ['HELLO', 'WORLD', 'PYTHON']

# Extract first character
first_chars = [word[0] for word in words]
print(first_chars) # ['h', 'w', 'p']

# Multiply numbers
numbers = [1, 2, 3, 4, 5]
doubled = [x * 2 for x in numbers]
print(doubled) # [2, 4, 6, 8, 10]

Filtering and transforming

Combine filtering and transformation:

# Square only even numbers
squares_of_evens = [x**2 for x in range(10) if x % 2 == 0]
print(squares_of_evens) # [0, 4, 16, 36, 64]

Multiple conditions

Use and to combine multiple conditions:

# Numbers divisible by 2 and 3
divisible_by_6 = [x for x in range(30) if x % 2 == 0 and x % 3 == 0]
print(divisible_by_6) # [0, 6, 12, 18, 24]

Nested comprehensions

List comprehensions can be nested:

# Flatten a matrix (2D list)
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [num for row in matrix for num in row]
print(flattened) # [1, 2, 3, 4, 5, 6, 7, 8, 9]

This is equivalent to:

flattened = []
for row in matrix:
for num in row:
flattened.append(num)

Complex examples

# Extract lengths of words longer than 3 characters
words = ['apple', 'an', 'banana', 'it', 'orange']
lengths = [len(word) for word in words if len(word) > 3]
print(lengths) # [5, 6, 6]

# Create tuples from two lists
names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 35]
pairs = [(name, age) for name, age in zip(names, ages)]
print(pairs) # [('Alice', 25), ('Bob', 30), ('Charlie', 35)]

Dictionary comprehensions

Dictionary comprehensions create dictionaries from iterables.

Basic syntax

{<key_expression>: <value_expression> for <item> in <iterable>}

Simple example

# Square each number as both key and value
squares = {x: x**2 for x in range(5)}
print(squares) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

From existing dictionary

Transform an existing dictionary:

# Uppercase all keys
original = {'name': 'Alice', 'age': 30, 'city': 'Boston'}
upper_keys = {k.upper(): v for k, v in original.items()}
print(upper_keys) # {'NAME': 'Alice', 'AGE': 30, 'CITY': 'Boston'}

With conditions

Filter items:

# Only keep even numbers as keys
evens = {x: x*2 for x in range(10) if x % 2 == 0}
print(evens) # {0: 0, 2: 4, 4: 8, 6: 12, 8: 16}

From other sequences

Create dictionaries from lists:

# Map words to their lengths
words = ['apple', 'banana', 'orange']
word_lengths = {word: len(word) for word in words}
print(word_lengths) # {'apple': 5, 'banana': 6, 'orange': 6}

# From list of tuples
pairs = [('a', 1), ('b', 2), ('c', 3)]
dict_from_pairs = {k: v for k, v in pairs}
print(dict_from_pairs) # {'a': 1, 'b': 2, 'c': 3}

Inverting a dictionary

Swap keys and values:

original = {'a': 1, 'b': 2, 'c': 3}
inverted = {v: k for k, v in original.items()}
print(inverted) # {1: 'a', 2: 'b', 3: 'c'}

Set comprehensions

Set comprehensions create sets from iterables (and automatically remove duplicates).

Basic syntax

{<expression> for <item> in <iterable>}

Simple example

# Set of squares
squares = {x**2 for x in range(5)}
print(squares) # {0, 1, 4, 9, 16}

# Extract unique first letters
words = ['apple', 'banana', 'apricot', 'orange']
first_letters = {word[0] for word in words}
print(first_letters) # {'a', 'b', 'o'} (unique letters)

With conditions

# Unique even squares
unique_evens = {x**2 for x in range(10) if x % 2 == 0}
print(unique_evens) # {0, 4, 16, 36, 64}

Removing duplicates

Sets automatically remove duplicates:

# Remove duplicates from a list
numbers = [1, 2, 2, 3, 3, 3, 4]
unique = {x for x in numbers}
print(unique) # {1, 2, 3, 4}

# Convert back to list if needed
unique_list = list(unique) # [1, 2, 3, 4] (order may vary)

Comparing comprehensions and loops

When to use comprehensions

Comprehensions are great when:

  • You're creating a new collection (list, dict, or set)
  • The transformation is simple and readable
  • You want concise, Pythonic code
# Good: Simple transformation
squares = [x**2 for x in range(10)]

# Good: Simple filtering
evens = [x for x in range(10) if x % 2 == 0]

When to use loops

Use loops when:

  • The logic is complex or has multiple steps
  • You need side effects (like printing) beyond creating a collection
  • You need to break or continue based on conditions
  • Readability would suffer with a comprehension
# Better with a loop: Complex logic
results = []
for x in range(10):
squared = x**2
if squared > 20:
print(f"Found large square: {squared}")
results.append(squared)
elif squared == 0:
continue
else:
results.append(squared)

Common patterns

Filtering lists

# Keep only positive numbers
numbers = [-2, -1, 0, 1, 2, 3]
positives = [x for x in numbers if x > 0]
print(positives) # [1, 2, 3]

# Keep only strings longer than 3
words = ['a', 'an', 'the', 'apple', 'banana']
long_words = [word for word in words if len(word) > 3]
print(long_words) # ['apple', 'banana']

Transforming lists

# Convert to strings
numbers = [1, 2, 3, 4, 5]
strings = [str(x) for x in numbers]
print(strings) # ['1', '2', '3', '4', '5']

# Apply a function to each item
def double(x):
return x * 2

numbers = [1, 2, 3, 4, 5]
doubled = [double(x) for x in numbers]
print(doubled) # [2, 4, 6, 8, 10]

Counting with dictionary comprehensions

# Count word lengths
words = ['apple', 'banana', 'orange', 'kiwi']
length_counts = {word: len(word) for word in words}
print(length_counts) # {'apple': 5, 'banana': 6, 'orange': 6, 'kiwi': 4}

Conditional expressions (ternary in comprehensions)

Use conditional expressions for more complex transformations:

# Mark numbers as 'even' or 'odd'
numbers = [1, 2, 3, 4, 5]
labels = ['even' if x % 2 == 0 else 'odd' for x in numbers]
print(labels) # ['odd', 'even', 'odd', 'even', 'odd']

# Square even numbers, cube odd numbers
transformed = [x**2 if x % 2 == 0 else x**3 for x in range(5)]
print(transformed) # [0, 1, 4, 27, 16]

Nested comprehensions

Comprehensions can be nested for multi-dimensional data:

List of lists

# Create a matrix
matrix = [[i*j for j in range(3)] for i in range(3)]
print(matrix) # [[0, 0, 0], [0, 1, 2], [0, 2, 4]]

Flattening nested structures

# Flatten a 2D list
matrix = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
flat = [num for row in matrix for num in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]

Dictionary from nested data

# Create a dictionary from nested tuples
data = [('Alice', ('age', 30)), ('Bob', ('age', 25))]
people = {name: {key: value} for name, (key, value) in data}
print(people) # {'Alice': {'age': 30}, 'Bob': {'age': 25}}

Best practices

Readability over brevity

# Good: Clear and readable
squares = [x**2 for x in range(10) if x % 2 == 0]

# Avoid: Too complex
result = [x**2 if x % 2 == 0 else x**3 if x > 5 else x for x in range(10) if x != 3]
# Better: Use a loop for complex logic

Use appropriate comprehensions

# List comprehension for lists
squares = [x**2 for x in range(10)]

# Set comprehension for unique items
unique_squares = {x**2 for x in range(10)}

# Dictionary comprehension for key-value pairs
square_dict = {x: x**2 for x in range(10)}

Don't use comprehensions for side effects

# Avoid: Comprehension with side effects
[print(x) for x in range(5)] # Works but not Pythonic

# Better: Use a loop
for x in range(5):
print(x)

Keep it simple

# Good: Simple transformation
words = ['hello', 'world']
upper = [w.upper() for w in words]

# Good: Simple filtering
evens = [x for x in range(10) if x % 2 == 0]

# Better as a loop: Complex logic
results = []
for item in data:
if item.is_valid():
processed = item.process()
if processed.success:
results.append(processed.value)

Performance considerations

Comprehensions are generally faster than equivalent loops because they're optimized internally:

# List comprehension (faster)
squares = [x**2 for x in range(1000)]

# Equivalent loop (slower)
squares = []
for x in range(1000):
squares.append(x**2)

However, readability should come first—use comprehensions when they make code clearer, not just because they're faster.

Summary

Comprehensions provide concise ways to create collections:

  • List comprehensions: [x for x in iterable]
  • Dictionary comprehensions: {k: v for k, v in items}
  • Set comprehensions: {x for x in iterable}
  • Support filtering with if clauses
  • Support conditional expressions for complex transformations
  • Can be nested for multi-dimensional data

Use comprehensions when they improve readability and create collections concisely. They're a Pythonic way to transform and filter data, but don't sacrifice clarity for brevity. When logic becomes complex, a regular loop is often clearer.