Dictionary Comprehensions in Python | Python Course | Dataplexa

Dictionary Comprehensions in Python

In Lesson 21, list comprehensions let you build lists in a single expressive line. Python applies exactly the same idea to dictionaries. A dictionary comprehension creates, transforms, or filters a dictionary without writing a loop, an empty dict, and manual key assignments. The syntax is nearly identical — you just use curly braces and provide both a key and a value expression.

Dictionary comprehensions appear constantly in real Python codebases — API response processing, data normalisation, configuration building, and lookup table construction. This lesson covers every pattern you will encounter.

The Problem Dictionary Comprehensions Solve

To map each word in a list to its length, the traditional loop approach needs four lines:

# Traditional loop
words   = ["apple", "banana", "cherry"]
lengths = {}
for w in words:
    lengths[w] = len(w)
print(lengths)

# Dictionary comprehension — same result, one line
lengths = {w: len(w) for w in words}
print(lengths)
{'apple': 5, 'banana': 6, 'cherry': 6} {'apple': 5, 'banana': 6, 'cherry': 6}

Both produce identical results. The comprehension reads naturally: "give me w: len(w) for each w in words."

Basic Syntax and Common Patterns

Structure: {key_expr: value_expr for item in iterable}. Both the key and value expressions are evaluated once per item.

# Map numbers to their squares
squares = {n: n ** 2 for n in range(1, 8)}
print(squares)

# Map numbers to their cubes
cubes = {n: n ** 3 for n in range(1, 6)}
print(cubes)

# Build a lookup: character → ASCII code
ascii_map = {c: ord(c) for c in "PYTHON"}
print(ascii_map)

# Build a lookup: user ID → username from a list of tuples
users = [(1, "alice"), (2, "bob"), (3, "carol")]
id_to_name = {uid: name for uid, name in users}
print(id_to_name)
{ 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49} {1: 1, 2: 8, 3: 27, 4: 64, 5: 125} {'P': 80, 'Y': 89, 'T': 84, 'H': 72, 'O': 79, 'N': 78} {1: 'alice', 2: 'bob', 3: 'carol'}
  • Curly braces {} with a colon make it a dict — without a colon it would be a set comprehension.
  • Keys must be hashable — strings, numbers, and tuples work; lists and dicts do not.
  • If the same key appears more than once, the last value wins — later iterations overwrite earlier ones.

Transforming an Existing Dictionary

Iterate directly over an existing dictionary using .items() to transform keys, values, or both — without touching the original.

costs = {"shirt": 12.00, "hat": 8.50, "bag": 22.00, "scarf": 6.00}

# Apply 40% markup to every price
retail = {item: round(price * 1.40, 2) for item, price in costs.items()}
print("Retail:", retail)

# Uppercase all keys
upper_keys = {k.upper(): v for k, v in costs.items()}
print("Upper:", upper_keys)

# Round all values to nearest pound
rounded = {k: round(v) for k, v in costs.items()}
print("Rounded:", rounded)

# Transform both key and value simultaneously
formatted = {k.title(): f"£{v:.2f}" for k, v in costs.items()}
print("Formatted:", formatted)
Retail: {'shirt': 16.8, 'hat': 11.9, 'bag': 30.8, 'scarf': 8.4} Upper: {'SHIRT': 12.0, 'HAT': 8.5, 'BAG': 22.0, 'SCARF': 6.0} Rounded: {'shirt': 12, 'hat': 9, 'bag': 22, 'scarf': 6} Formatted: {'Shirt': '£12.00', 'Hat': '£8.50', 'Bag': '£22.00', 'Scarf': '£6.00'}
  • dict.items() yields (key, value) pairs — unpack both directly in the for clause.
  • You can transform the key, the value, or both in the same expression.
  • The original dictionary is never modified — a new one is always returned.

Filtering with an if Clause

Add a trailing if condition to include only pairs that meet a test. Pairs where the condition is False are excluded from the new dictionary entirely.

scores = {"Alice": 88, "Bob": 53, "Carol": 76, "Dave": 45, "Eve": 92}

# Keep only students who passed (≥60)
passed = {name: score for name, score in scores.items() if score >= 60}
print("Passed:", passed)

# Keep only students who failed
failed = {name: score for name, score in scores.items() if score < 60}
print("Failed:", failed)

# Filter by key — keep only names starting with a vowel
vowel_names = {k: v for k, v in scores.items() if k[0].lower() in "aeiou"}
print("Vowel names:", vowel_names)

# Filter an inventory — keep only affordable in-stock items
inventory = {
    "pen":      {"price": 1.50, "stock": 200},
    "desk":     {"price": 350,  "stock": 0},
    "notebook": {"price": 4.99, "stock": 50},
    "lamp":     {"price": 24.99,"stock": 0},
    "ruler":    {"price": 0.99, "stock": 100},
}
available = {
    item: info["price"]
    for item, info in inventory.items()
    if info["stock"] > 0 and info["price"] < 10
}
print("Affordable & in stock:", available)
Passed: {'Alice': 88, 'Carol': 76, 'Eve': 92} Failed: {'Bob': 53, 'Dave': 45} Vowel names: {'Alice': 88, 'Eve': 92} Affordable & in stock: {'pen': 1.5, 'notebook': 4.99, 'ruler': 0.99}
  • Structure with filter: {k: v for k, v in d.items() if condition}
  • You can filter on the key, the value, or both — any valid boolean expression works.
  • Spreading a comprehension over multiple lines (using parentheses or natural indentation) is fine when readability requires it.

Building a Dictionary from Two Lists

A very common pattern is pairing two parallel lists — one of keys and one of values — into a dictionary using zip(). Use the comprehension form when you need to transform the keys or values while building.

products = ["coffee", "tea", "juice", "water"]
prices   = [3.50,    2.00,  4.25,   1.00  ]

# Basic pairing — comprehension or dict(zip()) both work
menu = {item: price for item, price in zip(products, prices)}
print("Menu:", menu)

# Comprehension adds power — transform while pairing
menu_formatted = {item.title(): f"${price:.2f}" for item, price in zip(products, prices)}
print("Formatted menu:", menu_formatted)

# Build from CSV header + data row
header = ["name",   "dept",        "salary"]
data   = ["Priya",  "Engineering", 90000  ]
record = {field: value for field, value in zip(header, data)}
print("Record:", record)

# Enumerate gives index + item — useful as a lookup
words = ["python", "data", "science"]
index_map = {word: i for i, word in enumerate(words)}
print("Index map:", index_map)
Menu: {'coffee': 3.5, 'tea': 2.0, 'juice': 4.25, 'water': 1.0} Formatted menu: {'Coffee': '$3.50', 'Tea': '$2.00', 'Juice': '$4.25', 'Water': '$1.00'} Record: {'name': 'Priya', 'dept': 'Engineering', 'salary': 90000} Index map: {'python': 0, 'data': 1, 'science': 2}
  • zip() stops at the shorter list if lengths differ — use zip_longest from itertools to fill missing values.
  • The CSV header + data row pattern is used constantly when reading raw CSV files without DictReader.
  • enumerate() pairs each item with its index — very useful for building position lookup tables.

Swapping Keys and Values

Inverting a dictionary — turning keys into values and values into keys — is a one-liner with dict comprehensions. Useful for building reverse lookup tables.

# Invert a dictionary — swap keys and values
country_code = {"US": "United States", "CA": "Canada", "MX": "Mexico", "IN": "India"}
code_country = {v: k for k, v in country_code.items()}
print(code_country)

# Real use — build a reverse word index
word_index = {"python": 0, "data": 1, "science": 2}
index_word = {v: k for k, v in word_index.items()}
print(index_word)

# Warning: if values are not unique, only the LAST key survives
grades = {"Alice": "A", "Bob": "B", "Carol": "A"}   # Alice and Carol share "A"
inverted = {v: k for k, v in grades.items()}
print(inverted)   # "A" maps to Carol (Bob's "A" overwrites Alice's)
{'United States': 'US', 'Canada': 'CA', 'Mexico': 'MX', 'India': 'IN'} {0: 'python', 1: 'data', 2: 'science'} {'A': 'Carol', 'B': 'Bob'}
  • Inversion works correctly only when all values are unique and hashable.
  • When values are not unique, later iterations silently overwrite earlier ones — the last key wins.
  • To preserve all keys for a shared value, use defaultdict(list) and append rather than overwrite.

Conditional Value Assignment — Ternary in the Value

Use an inline if/else in the value expression to assign different values based on a condition. Every key is still included — the ternary only decides which value it gets.

scores = {"Alice": 88, "Bob": 53, "Carol": 76, "Dave": 45, "Eve": 92}

# Label each student as pass or fail
results = {name: "pass" if score >= 60 else "fail"
           for name, score in scores.items()}
print(results)

# Assign a letter grade
grades = {
    name: "A" if score >= 90 else ("B" if score >= 75 else ("C" if score >= 60 else "F"))
    for name, score in scores.items()
}
print(grades)

# Apply tiered discount based on quantity
orders = {"pen": 50, "notebook": 8, "desk": 1, "chair": 3}
discounts = {item: 0.20 if qty >= 20 else (0.10 if qty >= 5 else 0.0)
             for item, qty in orders.items()}
print(discounts)
{'Alice': 'pass', 'Bob': 'fail', 'Carol': 'pass', 'Dave': 'fail', 'Eve': 'pass'} {'Alice': 'B', 'Bob': 'F', 'Carol': 'B', 'Dave': 'F', 'Eve': 'A'} {'pen': 0.2, 'notebook': 0.1, 'desk': 0.0, 'chair': 0.0}
  • The ternary goes in the value position — every key is included, the ternary picks the value.
  • This is different from a trailing if, which removes pairs entirely.
  • Keep nested ternaries to two levels maximum — move complex logic to a helper function beyond that.

Grouping Data with Dictionary Comprehensions

Combining set() or multiple comprehensions lets you group and reorganise data — a task that normally requires loops and defaultdict.

# Build a frequency map — count occurrences of each item
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
freq = {word: words.count(word) for word in set(words)}
print("Frequency:", freq)

# Normalise a dictionary — scale all values to 0–1 range
raw = {"speed": 120, "accuracy": 95, "memory": 80}
max_val = max(raw.values())
normalised = {k: round(v / max_val, 3) for k, v in raw.items()}
print("Normalised:", normalised)

# Process a list of API records into a lookup dict
api_records = [
    {"id": "u1", "name": "Priya",  "active": True},
    {"id": "u2", "name": "Kiran",  "active": False},
    {"id": "u3", "name": "Arjun",  "active": True},
]
# Build lookup: id → name, active users only
active_lookup = {r["id"]: r["name"] for r in api_records if r["active"]}
print("Active users:", active_lookup)
Frequency: {'cherry': 1, 'banana': 2, 'apple': 3} Normalised: {'speed': 1.0, 'accuracy': 0.792, 'memory': 0.667} Active users: {'u1': 'Priya', 'u3': 'Arjun'}
  • Using set(words) as the iterable gives unique words to count — avoiding duplicate keys.
  • Normalisation is a standard data science operation — scaling raw values to a common 0–1 range.
  • The API records pattern (list of dicts → keyed lookup) is one of the most frequent real-world uses of dict comprehensions.

Nested Dictionary Comprehensions

You can use a dict comprehension as the value expression inside another to build nested dictionaries in a single statement.

# Build a grade table: student → subject → default score
students = ["Alice", "Bob", "Carol"]
subjects = ["math", "science", "english"]

grade_table = {s: {sub: 0 for sub in subjects} for s in students}
for student, grades in grade_table.items():
    print(f"{student}: {grades}")

# Build a multiplication table
times_table = {r: {c: r * c for c in range(1, 6)} for r in range(1, 4)}
for row, cols in times_table.items():
    print(f"Row {row}: {cols}")
Alice: {'math': 0, 'science': 0, 'english': 0} Bob: {'math': 0, 'science': 0, 'english': 0} Carol: {'math': 0, 'science': 0, 'english': 0} Row 1: {1: 1, 2: 2, 3: 3, 4: 4, 5: 5} Row 2: {1: 2, 2: 4, 3: 6, 4: 8, 5: 10} Row 3: {1: 3, 2: 6, 3: 9, 4: 12, 5: 15}
  • The inner comprehension runs fresh for every iteration of the outer — each student gets an independent inner dictionary, not shared references.
  • Beyond one level of nesting, a regular loop is usually clearer to read and debug.

Quick Reference Table

PatternSyntaxWhat It Does
Basic build{k: v for x in iterable}Build a dict from any iterable
From dict{k: v for k, v in d.items()}Transform an existing dictionary
Filter{k: v for k, v in d.items() if cond}Keep only matching pairs
From two lists{k: v for k, v in zip(a, b)}Combine parallel lists
From enumerate{w: i for i, w in enumerate(lst)}Item → index lookup
Invert{v: k for k, v in d.items()}Swap keys and values
Ternary value{k: a if cond else b for ...}Conditional value assignment
Nested{k: {ik: iv ...} for ...}Build nested dictionaries

Practice

What punctuation marks a dictionary comprehension apart from a list comprehension?



Which dictionary method yields both keys and values for iteration?



What built-in function pairs two parallel lists before passing them to a dict comprehension?



Write the comprehension syntax to invert a dictionary called d.



What happens when two keys share the same value in a dictionary being inverted?



In a dict comprehension, what happens if the same key is generated more than once?



Quick Quiz

What does {x: x ** 2 for x in range(1, 4)} produce?






Which correctly filters a dictionary to keep only pairs where the value is greater than 10?





What does {v: k for k, v in {"a": 1, "b": 2}.items()} produce?





Which of the following is a valid key type in a dictionary comprehension?






What is the difference between a trailing if and a ternary if/else in a dict comprehension?





Given records = [{"id":"u1","name":"Priya","active":True},{"id":"u2","name":"Kiran","active":False},{"id":"u3","name":"Arjun","active":True}], what does {r["id"]: r["name"] for r in records if r["active"]} return?





NEXT UP
Advanced Sets in Python
Master set operations — union, intersection, difference, and symmetric difference — and learn why sets outperform lists for high-speed membership testing.