Free Python Comprehensions Cheat Sheet Online — Interactive Reference with Examples

·

Try the tool first:

Open the Interactive Python Comprehensions Cheat Sheet

40+ examples, real-time search, category filtering, one-click copy. 100% client-side, no signup.

Python comprehensions are one of the language's most beloved features — a single, elegant line that replaces multiple lines of boilerplate. Yet developers still hesitate: When should I use a generator instead of a list? How do I write an if-else inside a comprehension? Can I nest comprehensions? Why is my dict comprehension raising a KeyError?

This comprehensive guide covers every Python comprehension concept you need to know — from basic list comprehensions to advanced nested patterns, the walrus operator, and performance tradeoffs. Each section includes practical code examples you can copy and run immediately. By the end, you'll write Pythonic transformations with confidence — and you'll have a free interactive cheat sheet bookmarked for quick reference.

What Are Python Comprehensions?

A Python comprehension is a concise syntax for creating a new iterable (list, dict, set, or generator) from an existing iterable, applying an expression to each element and optionally filtering with a condition. Comprehensions are more compact, more readable, and often faster than equivalent for loops.

# The four types of comprehensions in Python

# 1. List Comprehension — returns a list
squares = [x**2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 2. Dict Comprehension — returns a dictionary
square_dict = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 3. Set Comprehension — returns a set (unique values)
square_set = {x**2 for x in range(10)}
# {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}

# 4. Generator Expression — returns a lazy iterator
square_gen = (x**2 for x in range(10))
# <generator object> — values computed on demand

Key Characteristics

  • Concise: One line replaces three to five lines of for-loop boilerplate.
  • Fast: Comprehensions run at C speed inside the interpreter, faster than pure Python loops.
  • Functional: No side effects — the expression produces a new collection without mutating the source.
  • Composable: Multiple for clauses and conditions can be chained for complex transformations.

The readability rule: Comprehensions are great for simple transformations. If your comprehension spans multiple lines or uses deeply nested logic, a regular for loop is usually more readable. Guido van Rossum calls this the "one obvious way" principle.

List Comprehensions

List comprehensions are the most common type. They create a new list by evaluating an expression for each element in an existing iterable.

Basic Syntax

The simplest form: [expression for item in iterable]

# Basic list comprehension
numbers = [1, 2, 3, 4, 5]
doubled = [x * 2 for x in numbers]
# [2, 4, 6, 8, 10]

# With range
squares = [x**2 for x in range(6)]
# [0, 1, 4, 9, 16, 25]

# With string
chars = [c.upper() for c in "hello"]
# ['H', 'E', 'L', 'L', 'O']

# With enumerate (unpacking in the expression)
indexed = [f"{i}: {v}" for i, v in enumerate(["a", "b", "c"])]
# ['0: a', '1: b', '2: c']

Filtering with if

Add a condition after the for clause to filter elements: [expression for item in iterable if condition]

numbers = [1, -2, 3, -4, 5, -6]

# Keep only positive numbers
positive = [x for x in numbers if x > 0]
# [1, 3, 5]

# Keep only even numbers
evens = [x for x in range(20) if x % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# Filter strings by length
words = ["apple", "kiwi", "banana", "fig"]
long_words = [w for w in words if len(w) > 4]
# ['apple', 'banana']

# Multiple chained conditions (AND logic)
nums = range(100)
result = [x for x in nums if x > 10 if x % 2 == 0 if x % 3 == 0]
# [12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96]

if-else Conditional Expression

Use a ternary expression before the for clause to transform every element conditionally: [expr_if_true if condition else expr_if_false for item in iterable]

numbers = [1, -2, 3, -4, 5]

# Replace negatives with 0
clamped = [x if x > 0 else 0 for x in numbers]
# [1, 0, 3, 0, 5]

# Label numbers as even/odd
labels = ["even" if x % 2 == 0 else "odd" for x in range(5)]
# ['even', 'odd', 'even', 'odd', 'even']

# Categorize scores
scores = [85, 92, 67, 45, 78]
grades = ["A" if s >= 90 else "B" if s >= 80 else "C" if s >= 70 else "D" for s in scores]
# ['B', 'A', 'D', 'D', 'C']

Multiple for Clauses (Nested Iteration)

Chain multiple for clauses to iterate over nested structures. The rightmost for is the innermost loop.

# Flatten a matrix
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [item for row in matrix for item in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

# Equivalent to:
# flat = []
# for row in matrix:
#     for item in row:
#         flat.append(item)

# Cartesian product
colors = ["red", "green"]
sizes = ["S", "M", "L"]
products = [f"{c}-{s}" for c in colors for s in sizes]
# ['red-S', 'red-M', 'red-L', 'green-S', 'green-M', 'green-L']

# Multiple iterables with zip
names = ["Alice", "Bob"]
ages = [30, 25]
pairs = [f"{n} is {a}" for n, a in zip(names, ages)]
# ['Alice is 30', 'Bob is 25']

Using Functions and Methods

words = ["  Hello  ", "WORLD", "  Python  "]

# Strip and lowercase
clean = [w.strip().lower() for w in words]
# ['hello', 'world', 'python']

# Using built-in functions
nums = [1.2, 2.7, 3.1]
integers = [round(n) for n in nums]
# [1, 3, 3]

# Object method calls
class User:
    def __init__(self, name): self.name = name
    def greet(self): return f"Hello, {self.name}!"

users = [User("Alice"), User("Bob")]
greetings = [u.greet() for u in users]
# ['Hello, Alice!', 'Hello, Bob!']

Dict Comprehensions

Dict comprehensions create dictionaries using the syntax {key: value for item in iterable}.

Basic Syntax

# Basic dict comprehension
squares = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# From two lists using zip
keys = ["a", "b", "c"]
values = [1, 2, 3]
merged = {k: v for k, v in zip(keys, values)}
# {'a': 1, 'b': 2, 'c': 3}

# With enumerate
indexed = {i: v for i, v in enumerate(["apple", "banana"])}
# {0: 'apple', 1: 'banana'}

Filtering with if

# Filter by value
scores = {"Alice": 85, "Bob": 92, "Charlie": 67, "Diana": 78}
passed = {name: score for name, score in scores.items() if score >= 80}
# {'Alice': 85, 'Bob': 92}

# Filter by key
config = {"debug_mode": True, "api_key": "secret", "timeout": 30}
public = {k: v for k, v in config.items() if not k.endswith("_key")}
# {'debug_mode': True, 'timeout': 30}

# Multiple conditions
users = {"alice": 25, "bob": 17, "charlie": 30, "diana": 15}
adults = {name: age for name, age in users.items() if age >= 18 if len(name) > 3}
# {'alice': 25, 'charlie': 30}

Transforming Existing Dicts

# Invert a dictionary (values become keys)
d = {"a": 1, "b": 2, "c": 3}
inverted = {v: k for k, v in d.items()}
# {1: 'a', 2: 'b', 3: 'c'}

# Caution: duplicate values will be lost!
d2 = {"a": 1, "b": 1}
# inverted → {1: 'b'}  ('a' is overwritten)

# Convert values
prices = {"apple": 1.5, "banana": 0.8, "cherry": 2.5}
cents = {k: int(v * 100) for k, v in prices.items()}
# {'apple': 150, 'banana': 80, 'cherry': 250}

# Conditional transformation
scores = {"Alice": 85, "Bob": 92, "Charlie": 67}
grades = {
    name: ("A" if s >= 90 else "B" if s >= 80 else "C")
    for name, s in scores.items()
}
# {'Alice': 'B', 'Bob': 'A', 'Charlie': 'C'}

Nested Dict Comprehensions

# Create a multiplication table as nested dict
table = {
    i: {j: i * j for j in range(1, 4)}
    for i in range(1, 4)
}
# {
#   1: {1: 1, 2: 2, 3: 3},
#   2: {1: 2, 2: 4, 3: 6},
#   3: {1: 3, 2: 6, 3: 9}
# }

# Flatten nested dict (one level)
nested = {"a": {"x": 1, "y": 2}, "b": {"x": 3, "y": 4}}
flat = {f"{outer}_{inner}": val
        for outer, inner_dict in nested.items()
        for inner, val in inner_dict.items()}
# {'a_x': 1, 'a_y': 2, 'b_x': 3, 'b_y': 4}

Set Comprehensions

Set comprehensions create sets (unique, unordered collections) using curly braces with a single expression: {expression for item in iterable}.

# Basic set comprehension
nums = [1, 2, 2, 3, 3, 3, 4]
unique_squares = {x**2 for x in nums}
# {1, 4, 9, 16}

# Deduplicate and normalize
words = ["Apple", "apple", "APPLE", "Banana", "banana"]
unique = {w.lower() for w in words}
# {'apple', 'banana'}

# Filter with condition
even_squares = {x**2 for x in range(20) if x % 2 == 0}
# {0, 4, 16, 36, 64, 100, 144, 196, 256, 324}

# Set comprehension vs set() constructor
from_list = set([x**2 for x in range(10)])      # Creates list first, then set
from_comp = {x**2 for x in range(10)}           # Directly creates set — slightly faster

Mathematical Operations via Set Comprehension

# Multiples
multiples_3 = {x for x in range(50) if x % 3 == 0}

# Prime numbers (simple sieve)
primes = {x for x in range(2, 50) if all(x % y != 0 for y in range(2, int(x**0.5) + 1))}

# Character sets from strings
vowels = {"a", "e", "i", "o", "u"}
text = "Hello World"
found_vowels = {c.lower() for c in text if c.lower() in vowels}
# {'e', 'o'}

Generator Expressions

Generator expressions use parentheses instead of brackets and return a lazy iterator. They evaluate elements one at a time on demand, making them incredibly memory-efficient for large datasets.

Basic Syntax

# Generator expression
gen = (x**2 for x in range(10))
# <generator object>

# Consume with list()
list(gen)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# Consume in a loop (most common)
for val in (x**2 for x in range(10)):
    print(val)

# Generator expressions can only be iterated once!
gen = (x**2 for x in range(5))
list(gen)  # [0, 1, 4, 9, 16]
list(gen)  # [] — exhausted!

Memory Efficiency

# List comprehension — stores all values in memory
squares_list = [x**2 for x in range(10_000_000)]  # Uses ~80MB

# Generator expression — almost zero memory
squares_gen = (x**2 for x in range(10_000_000))   # Uses ~0MB

# Sum without storing all values
total = sum(x**2 for x in range(10_000_000))
# Computes each square, adds to sum, discards — memory constant

# Find first match without scanning everything
first_big = next((x for x in range(1000000) if x**2 > 500000), None)
# Stops at x=708 — does not evaluate remaining 999,292 elements

Chaining Generator Expressions

# Chain multiple transformations as generators
nums = range(1000000)

# Step 1: filter positives
positive = (x for x in nums if x > 0)

# Step 2: square them
squared = (x**2 for x in positive)

# Step 3: filter large results
large = (x for x in squared if x > 10000)

# Nothing computed yet! Only when we iterate:
result = list(large)[:5]
# [10201, 10404, 10609, 10816, 11025]

# Or chain in one expression
result = list(
    x**2 for x in (x for x in range(1000) if x > 0)
    if x**2 > 10000
)[:5]

Using next() with Default

# Find first even number
first_even = next((x for x in [1, 3, 5, 8, 9] if x % 2 == 0), None)
# 8

# No match — returns default
first_negative = next((x for x in [1, 3, 5] if x < 0), 0)
# 0

# Default can be any value
first_admin = next(
    (u for u in users if u.role == "admin"),
    User("default", "guest")
)

Conditionals and Logic

Single if Filter

Placed after the for clause. Elements that fail the condition are skipped.

[x for x in items if x > 0]

if-else Expression

Placed before the for clause. Every element is transformed; the condition chooses which expression to use.

[x if x > 0 else 0 for x in items]

Multiple if Filters (AND Logic)

Multiple if clauses after the for clause act as logical AND.

[x for x in range(100) if x > 10 if x % 2 == 0 if x % 3 == 0]
# Equivalent to: x > 10 and x % 2 == 0 and x % 3 == 0

Walrus Operator (Python 3.8+)

The := walrus operator lets you assign and test in one expression, perfect for expensive computations or pattern matching inside comprehensions.

import re
lines = ["Order 123", "No number here", "Invoice 456", "Error"]

# Extract numbers only from lines that contain them
numbers = [m.group(0) for line in lines if (m := re.search(r'\d+', line))]
# ['123', '456']

# Without walrus (requires two passes or helper function)
# With walrus: one pass, clean and Pythonic

# Another example: cache expensive computation
values = [1, 4, 9, 16, 25]
sqrt_results = [s for x in values if (s := x**0.5) > 2]
# [2.0, 3.0, 4.0, 5.0]

Nested Comprehensions

Nested comprehensions can be written with multiple for clauses in a single comprehension, or as comprehensions inside comprehensions.

Multiple for Clauses (Preferred)

# Flatten a matrix — multiple for clauses
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [item for row in matrix for item in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

# With condition on inner element
filtered_flat = [item for row in matrix for item in row if item > 5]
# [6, 7, 8, 9]

# With condition on outer element
first_two = [item for row in matrix if len(row) == 3 for item in row[:2]]
# [1, 2, 4, 5, 7, 8]

Nested Comprehension (Comprehension Inside Comprehension)

# Transpose a matrix
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = [[row[i] for row in matrix] for i in range(3)]
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

# Equivalent with zip (usually clearer)
transposed = list(map(list, zip(*matrix)))

# Generate Pascal's triangle rows
pascal = [[1 if j == 0 or j == i else row[j-1] + row[j]
           for j in range(i + 1)]
          for i, row in enumerate([[1]] * 6)]
# Simplified:
def pascal_row(n):
    row = [1]
    for _ in range(n):
        row = [x + y for x, y in zip([0] + row, row + [0])]
    return row

[pascal_row(i) for i in range(5)]
# [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]]

When to Use Nested vs Flat

# Nested comprehension — result is nested structure
matrix = [[i * j for j in range(5)] for i in range(5)]
# 5x5 multiplication table

# Flat comprehension with multiple for clauses — result is flat
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)]

Performance and Best Practices

List Comprehension vs For Loop

import timeit

# For loop
loop_code = '''
result = []
for x in range(10000):
    result.append(x**2)
'''

# List comprehension
comp_code = '[x**2 for x in range(10000)]'

# Generator expression
gen_code = 'list(x**2 for x in range(10000))'

print(timeit.timeit(loop_code, number=1000))
# ~0.85 seconds

print(timeit.timeit(comp_code, number=1000))
# ~0.55 seconds — ~35% faster

print(timeit.timeit(gen_code, number=1000))
# ~0.60 seconds — slightly slower than list comp due to generator overhead

List Comprehension vs map()

# map() with lambda
mapped = list(map(lambda x: x**2, range(10000)))

# List comprehension
comped = [x**2 for x in range(10000)]

# map() is slightly slower in Python 3 because of lambda overhead
# List comprehensions are generally preferred for readability
# map() can be faster with an existing function:
mapped = list(map(str, range(10000)))  # str is a built-in

Memory Comparison

import sys

# List comprehension — full list in memory
lst = [x**2 for x in range(1_000_000)]
print(sys.getsizeof(lst))  # ~8,000,000 bytes

# Generator expression — almost zero memory
gen = (x**2 for x in range(1_000_000))
print(sys.getsizeof(gen))  # ~112 bytes

# Dict comprehension
 d = {x: x**2 for x in range(1_000_000)}
print(sys.getsizeof(d))  # ~50,000,000 bytes

Common Patterns

Frequency Counting

Dict comprehensions are perfect for counting occurrences.

words = ["apple", "banana", "apple", "cherry", "banana", "apple"]

# Using dict comprehension with count()
freq = {word: words.count(word) for word in set(words)}
# {'apple': 3, 'banana': 2, 'cherry': 1}

# More efficient with collections.Counter
from collections import Counter
freq = dict(Counter(words))
# Same result, but Counter is O(n) vs O(n²) for count() in comprehension

Grouping

from collections import defaultdict

people = [("Alice", "Engineering"), ("Bob", "Sales"), ("Charlie", "Engineering")]

# Group by department using defaultdict
groups = defaultdict(list)
for name, dept in people:
    groups[dept].append(name)

# Or with dict comprehension (less efficient for large data)
groups = {dept: [name for name, d in people if d == dept]
          for _, dept in people}
# {'Engineering': ['Alice', 'Charlie'], 'Sales': ['Bob']}

Flattening Nested Structures

# Flatten list of lists
nested = [[1, 2], [3, 4], [5, 6]]
flat = [item for sublist in nested for item in sublist]
# [1, 2, 3, 4, 5, 6]

# Flatten mixed-depth (one level)
mixed = [1, [2, 3], [4, [5, 6]]]
flat = [item for sub in mixed for item in (sub if isinstance(sub, list) else [sub])]
# [1, 2, 3, 4, [5, 6]] — only flattens one level

Removing Duplicates While Preserving Order

items = [3, 1, 2, 3, 2, 4, 1, 5]

# Dict comprehension trick (Python 3.7+ preserves insertion order)
unique = list({x: None for x in items}.keys())
# [3, 1, 2, 4, 5]

# Equivalent but clearer
def unique_ordered(seq):
    seen = set()
    return [x for x in seq if not (x in seen or seen.add(x))]

unique_ordered(items)
# [3, 1, 2, 4, 5]

Dictionary from Object Attributes

class User:
    def __init__(self, name, age, role):
        self.name = name
        self.age = age
        self.role = role

user = User("Alice", 30, "Engineer")

# Convert to dict (excluding private attributes)
user_dict = {k: v for k, v in user.__dict__.items() if not k.startswith('_')}
# {'name': 'Alice', 'age': 30, 'role': 'Engineer'}

Comparison Tables

List Comprehension vs For Loop vs map/filter

FeatureList CompFor Loopmap/filter
Syntax[x*2 for x in items]Multi-linelist(map(lambda x: x*2, items))
SpeedFastestSlowerSlower (lambda overhead)
ReadabilityExcellentGoodPoor (lambda)
FilteringBuilt-in ifif statementfilter() + map()
Side effectsDiscouragedNaturalDiscouraged
Nested loopsCleanVerboseVery messy

List Comp vs Generator vs Set Comp vs Dict Comp

FeatureList CompGeneratorSet CompDict Comp
Syntax[...](...){...}{k: v}
Resultlistiteratorsetdict
MemoryO(n)O(1)O(n)O(n)
EvaluationEagerLazyEagerEager
ReusableYesNo (once)YesYes
DuplicatesKeptKeptRemovedLast wins
Best forSmall/medium dataLarge data, one passUniquenessKey-value mapping

Pro Tips and Gotchas

Gotcha: Don't use comprehensions for side effects. A comprehension should produce a value. If you find yourself writing [print(x) for x in items] or [f.write(x) for x in items], use a for loop instead. The list of None values is wasteful and confusing.

Tip: Keep comprehensions readable. If a comprehension spans more than one line or uses deeply nested logic, a regular loop with helper variables is usually clearer. Readability counts more than brevity.

Tip: Use generator expressions for large data. When processing files, database results, or streams, generator expressions keep memory usage constant. sum(x.price for x in orders) works on millions of rows without loading them all.

Tip: Walrus operator for pattern matching. Python 3.8's := operator is powerful inside comprehensions: [m.group(0) for line in lines if (m := re.search(r'\d+', line))]. It eliminates the need for two-pass logic or helper functions.

Tip: Avoid complex if-else chains. A comprehension with multiple nested ternary operators is hard to read. If you need x if a else y if b else z if c else w, consider a helper function or a loop with explicit branches.

Tip: Nested comprehensions are not always better. [[row[i] for row in matrix] for i in range(3)] transposes a matrix, but list(map(list, zip(*matrix))) is often clearer. Choose the version that future readers will understand fastest.

Found this useful? Check out our free developer tools or browse more articles.