Python Dictionary Comprehension: Syntax, If/Else, and Nesting

⏱️ 3 min read 🐍 Python

A Python dictionary comprehension builds a dict in a single expression: {key: value for item in iterable}. It replaces the three-line "create empty dict, loop, assign" pattern with one readable line, and it's usually faster too. The only real trap is conditional placement — if at the end filters items, while if/else belongs in the value expression.

Quick answer: A dictionary comprehension creates a dict with the syntax {key_expr: value_expr for item in iterable}. Add if condition at the end to filter which items are included; put value_if_true if condition else value_if_false before the for to choose between two values. Example: {x: x**2 for x in range(5)} gives {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}.

What is the basic syntax of a dictionary comprehension?

The syntax has four parts: opening braces, a key: value expression pair, a for clause naming the loop variable and iterable, and an optional if filter. The colon between key and value is what makes it a dict comprehension rather than a set comprehension — {x for x in ...} without a colon builds a set.

#  {  KEY : VALUE   for VARIABLE in ITERABLE  }
#     ───   ─────       ────────    ────────
squares = {x: x**2 for x in range(5)}
# Output: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# Traditional loop equivalent
squares = {}
for x in range(5):
    squares[x] = x ** 2

Both key and value can be any expression — method calls, arithmetic, even other comprehensions. If the same key is produced twice, the last value wins silently.

Where does if/else go in a dictionary comprehension?

This is the classic confusion. A trailing if (after the iterable) filters — items that fail the test are dropped entirely. An if/else is a conditional expression that belongs in the value position (before the for) — every item is kept, but its value depends on the condition. There is no trailing else; {x: x for x in nums if x > 0 else 0} is a SyntaxError.

# FILTER: trailing if — drops odd numbers entirely
evens = {x: x**2 for x in range(10) if x % 2 == 0}
# Output: {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}

# MAP: if/else in the value — keeps every key
labels = {x: 'even' if x % 2 == 0 else 'odd' for x in range(5)}
# Output: {0: 'even', 1: 'odd', 2: 'even', 3: 'odd', 4: 'even'}

# BOTH: filter first, then choose the value
result = {x: ('big' if x > 5 else 'small')
          for x in range(10) if x != 0}

See if/else in dictionary comprehensions for a deeper walkthrough of the placement rules and error messages.

How do you write a nested dictionary comprehension?

Put a second comprehension in the value position. The outer comprehension produces the top-level keys; the inner one builds each value dict. Read it outside-in: "for each i, build a dict of j values." Two levels is the practical readability limit — beyond that, use a loop.

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

# Flatten a nested dict back down
flat = {f"{i}x{j}": v
        for i, row in table.items()
        for j, v in row.items()}
# Output: {'1x1': 1, '1x2': 2, ..., '3x3': 9}

More patterns (inverting nesting, filtering inner dicts) in nested dictionary comprehensions.

Is a dict comprehension better than map() or dict(zip())?

For pairing two existing lists unchanged, dict(zip(keys, values)) is the fastest and clearest option. Use a comprehension the moment you need to transform or filter while building — map() plus dict() forces a lambda and reads worse. Comprehensions also beat the loop-and-assign pattern because the dict is built with an optimized bytecode path.

keys = ['name', 'age', 'city']
values = ['Alice', 30, 'NYC']

# No transformation needed → dict(zip()) wins
person = dict(zip(keys, values))
# {'name': 'Alice', 'age': 30, 'city': 'NYC'}

# Transformation needed → comprehension wins
person = {k.upper(): v for k, v in zip(keys, values)}

# map() equivalent — works, but harder to read
person = dict(map(lambda kv: (kv[0].upper(), kv[1]),
                  zip(keys, values)))

Performance notes: a comprehension is typically 20–40% faster than an explicit loop for the same work (no repeated squares[x] = ... name lookups). Memory-wise a comprehension always materializes the whole dict; if you only iterate once over huge data, a generator feeding dict() saves nothing here — dicts aren't lazy. The same syntax rules apply to list comprehensions, just with square brackets and no colon.

Transform Existing Dictionaries

Swap Keys and Values

original = {'a': 1, 'b': 2, 'c': 3}
swapped = {v: k for k, v in original.items()}
# Output: {1: 'a', 2: 'b', 3: 'c'}
# Warning: duplicate values collapse — last key wins

Filter a Dictionary

prices = {'apple': 0.50, 'banana': 0.30, 'orange': 0.80, 'grape': 0.20}

# Only items over $0.40
expensive = {k: v for k, v in prices.items() if v > 0.40}
# Output: {'apple': 0.50, 'orange': 0.80}

Modify Values

prices = {'apple': 0.50, 'banana': 0.30, 'orange': 0.80}

# Apply 10% discount
discounted = {k: v * 0.9 for k, v in prices.items()}
# Output: {'apple': 0.45, 'banana': 0.27, 'orange': 0.72}

Iterating with .items() is the standard pattern here — see essential dictionary methods for the full toolkit.

Practical Examples

Word Frequency Counter

text = "the quick brown fox jumps over the lazy dog"
words = text.split()

# Count word frequencies
freq = {word: words.count(word) for word in set(words)}
# Output: {'the': 2, 'quick': 1, 'brown': 1, ...}
# (For large texts, collections.Counter(words) is O(n) instead of O(n²))

Group Data by Category

students = [
    {'name': 'Alice', 'grade': 'A'},
    {'name': 'Bob', 'grade': 'B'},
    {'name': 'Charlie', 'grade': 'A'}
]

# Group names by grade
by_grade = {
    grade: [s['name'] for s in students if s['grade'] == grade]
    for grade in set(s['grade'] for s in students)
}
# Output: {'A': ['Alice', 'Charlie'], 'B': ['Bob']}

Clean and Normalize Data

raw_data = {'  Name ': 'Alice', 'AGE  ': '30', ' City': 'NYC  '}

# Clean keys and values
clean = {k.strip().lower(): v.strip() for k, v in raw_data.items()}
# Output: {'name': 'Alice', 'age': '30', 'city': 'NYC'}

When to Use Dict Comprehensions

Pro Tip: Dict comprehensions are great for simple transformations. For complex logic, use regular loops for better readability. Remember: readable code beats clever one-liners!

← Back to Python Tips