Python Dictionary Comprehension: Syntax, If/Else, and Nesting
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
- Creating dictionaries from sequences
- Transforming or renaming keys/values of an existing dict
- Filtering dictionary items
- Simple one-to-one mappings with light logic
- Not for: multi-step logic, side effects, or anything needing a comment to explain — use a loop
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