Pythonic Patterns
Dictionary Comprehension
Concise Dict Creation
You need to create a lookup table mapping names to ages. A loop and dict build-up
works, but {name: age for name, age in pairs} does it in one line. Dict
comprehensions are the idiomatic way to build dictionaries from iterables.
Basic key-value creation
Create a dictionary from a sequence.
# Basic Dictionary Comprehension
print("=== Basic Dictionary Comprehension ===\n")
# Traditional loop approach
squares_loop = {}
for x in range(1, 6):
squares_loop[x] = x ** 2
print(f"Loop: {squares_loop}")
# Dictionary comprehension
squares_comp = {x: x ** 2 for x in range(1, 6)}
print(f"Comprehension: {squares_comp}")
print("\n=== Key: Value Structure ===")
# Number to its word
num_words = {1: "one", 2: "two", 3: "three"}
print(f"Manual: {num_words}")
# Same pattern with comprehension
words = ["zero", "one", "two", "three", "four"]
num_to_word = {i: words[i] for i in range(len(words))}
print(f"Comprehension: {num_to_word}")
print("\n=== From Strings ===")
# Character positions
word = "hello"
char_positions = {char: i for i, char in enumerate(word)}
print(f"'{word}' char positions: {char_positions}")
# Note: duplicate chars get last position
word2 =
char_pos2 = {char: i for i, char in enumerate(word2)}
print(f"'{word2}' char positions: {char_pos2}")
print("\n=== From Other Collections ===")
# Set to length mapping
names = {"Alice", "Bob", "Charlie"}
name_lengths = {name: len(name) for name in names}
print(f"Names: {names}")
print(f"Lengths: {name_lengths}")
print("\n=== Syntax Summary ===")
print("{key_expr: value_expr for item in iterable}")
print(" ^^^^^^^^ ^^^^^^^^^^ ^^^^ ^^^^^^^^")
print(" key value var source")
# Basic Dictionary Comprehension
print("=== Basic Dictionary Comprehension ===\n")
# Traditional loop approach
squares_loop = {}
for x in range(1, 6):
squares_loop[x] = x ** 2
print(f"Loop: {squares_loop}")
# Dictionary comprehension
squares_comp = {x: x ** 2 for x in range(1, 6)}
print(f"Comprehension: {squares_comp}")
print("\n=== Key: Value Structure ===")
# Number to its word
num_words = {1: "one", 2: "two", 3: "three"}
print(f"Manual: {num_words}")
# Same pattern with comprehension
words = ["zero", "one", "two", "three", "four"]
num_to_word = {i: words[i] for i in range(len(words))}
print(f"Comprehension: {num_to_word}")
print("\n=== From Strings ===")
# Character positions
word = "hello"
char_positions = {char: i for i, char in enumerate(word)}
print(f"'{word}' char positions: {char_positions}")
# Note: duplicate chars get last position
word2 =
char_pos2 = {char: i for i, char in enumerate(word2)}
print(f"'{word2}' char positions: {char_pos2}")
print("\n=== From Other Collections ===")
# Set to length mapping
names = {"Alice", "Bob", "Charlie"}
name_lengths = {name: len(name) for name in names}
print(f"Names: {names}")
print(f"Lengths: {name_lengths}")
print("\n=== Syntax Summary ===")
print("{key_expr: value_expr for item in iterable}")
print(" ^^^^^^^^ ^^^^^^^^^^ ^^^^ ^^^^^^^^")
print(" key value var source")
# Basic Dictionary Comprehension
print("=== Basic Dictionary Comprehension ===\n")
# Traditional loop approach
squares_loop = {}
for x in range(1, 6):
squares_loop[x] = x ** 2
print(f"Loop: {squares_loop}")
# Dictionary comprehension
squares_comp = {x: x ** 2 for x in range(1, 6)}
print(f"Comprehension: {squares_comp}")
print("\n=== Key: Value Structure ===")
# Number to its word
num_words = {1: "one", 2: "two", 3: "three"}
print(f"Manual: {num_words}")
# Same pattern with comprehension
words = ["zero", "one", "two", "three", "four"]
num_to_word = {i: words[i] for i in range(len(words))}
print(f"Comprehension: {num_to_word}")
print("\n=== From Strings ===")
# Character positions
word = "hello"
char_positions = {char: i for i, char in enumerate(word)}
print(f"'{word}' char positions: {char_positions}")
# Note: duplicate chars get last position
word2 =
char_pos2 = {char: i for i, char in enumerate(word2)}
print(f"'{word2}' char positions: {char_pos2}")
print("\n=== From Other Collections ===")
# Set to length mapping
names = {"Alice", "Bob", "Charlie"}
name_lengths = {name: len(name) for name in names}
print(f"Names: {names}")
print(f"Lengths: {name_lengths}")
print("\n=== Syntax Summary ===")
print("{key_expr: value_expr for item in iterable}")
print(" ^^^^^^^^ ^^^^^^^^^^ ^^^^ ^^^^^^^^")
print(" key value var source")
{key: value for item in iterable} - define both key and value.
From parallel lists
Combine two lists into a dictionary.
# Dictionary from Parallel Lists
print("=== Creating Dict from Two Lists ===\n")
# Two parallel lists
keys = ["name", "age", "city"]
values = ["Alice", 30, "NYC"]
print(f"Keys: {keys}")
print(f"Values: {values}")
# Using zip() with dict comprehension
person = {k: v for k, v in zip(keys, values)}
print(f"Dict: {person}")
# Shortcut: dict() constructor
person2 = dict(zip(keys, values))
print(f"dict(zip()): {person2}")
print("\n=== Multiple Records ===")
# Employee data as parallel lists
ids = [101, 102, 103]
names = ["Alice", "Bob", "Charlie"]
salaries = [50000, 60000, 55000]
# ID to name mapping
id_to_name = {i: n for i, n in zip(ids, names)}
print(f"ID → Name: {id_to_name}")
# ID to (name, salary) tuple
id_to_info = {i: (n, s) for i, n, s in zip(ids, names, salaries)}
print(f"ID → Info: {id_to_info}")
print("\n=== Index-Based Keys ===")
fruits = ["apple", "banana", "cherry"]
# Index as key
indexed = {i: fruit for i, fruit in enumerate(fruits)}
print(f"Indexed: {indexed}")
# Starting from 1
indexed_from_1 = {i: fruit for i, fruit in enumerate(fruits, start=1)}
print(f"From 1: {indexed_from_1}")
print("\n=== Unequal Lengths ===")
short = [1, 2]
long = ['a', 'b', 'c', 'd']
# zip stops at shorter
result = {k: v for k, v in zip(short, long)}
print(f"short={short}, long={long}")
print(f"zip result: {result}")
Use zip() to pair up keys and values: {k: v for k, v in zip(keys, values)}.
Filter entries
Include only certain key-value pairs.
# Filtering in Dictionary Comprehension
print("=== Filtering Dictionary Items ===\n")
# Original dictionary
prices = {"apple": 1.50, "banana": 0.75, "cherry": 3.00, "date": 2.25, "elderberry": 4.50}
print(f"All prices: {prices}")
# Filter by value: expensive items (> $2)
expensive = {k: v for k, v in prices.items() if v > 2.00}
print(f"Expensive (>$2): {expensive}")
# Filter by key: names starting with vowel
vowel_fruits = {k: v for k, v in prices.items() if k[0] in 'aeiou'}
print(f"Vowel start: {vowel_fruits}")
print("\n=== Filtering from Source Dict ===")
scores = {"Alice": 85, "Bob": 92, "Charlie": 78, "Diana": 95, "Eve": 65}
print(f"All scores: {scores}")
# Passing students (>= 70)
passing = {name: score for name, score in scores.items() if score >= 70}
print(f"Passing: {passing}")
# Top performers (>= 90)
top = {name: score for name, score in scores.items() if score >= 90}
print(f"Top (>=90): {top}")
print("\n=== Multiple Conditions ===")
products = {
"laptop": {"price": 999, "stock": 5},
"phone": {"price": 699, "stock": 0},
"tablet": {"price": 399, "stock": 10},
"watch": {"price": 299, "stock": 3},
}
# In stock AND affordable (< $500)
available_affordable = {
name: info
for name, info in products.items()
if info["stock"] > 0 and info["price"] < 500
}
print(f"Available & <$500: {available_affordable}")
print("\n=== Filtering with External Data ===")
# Keep only selected keys
all_data = {"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}
keep = {"a", "c", "e"}
filtered = {k: v for k, v in all_data.items() if k in keep}
print(f"All: {all_data}")
print(f"Keep {keep}: {filtered}")
Add if condition to filter: {k: v for k, v in items if v > 0}.
Transform keys and values
Modify keys, values, or both.
# Transforming Keys and Values
print("=== Transforming Values ===\n")
# Original prices
prices = {"apple": 1.50, "banana": 0.75, "cherry": 3.00}
print(f"Original: {prices}")
# Apply 10% discount
discounted = {k: round(v * 0.9, 2) for k, v in prices.items()}
print(f"10% off: {discounted}")
# Convert to cents
cents = {k: int(v * 100) for k, v in prices.items()}
print(f"In cents: {cents}")
print("\n=== Transforming Keys ===")
# Original with lowercase keys
data = {"name": "Alice", "age": 30, "city": "NYC"}
print(f"Original: {data}")
# Uppercase keys
upper_keys = {k.upper(): v for k, v in data.items()}
print(f"Upper keys: {upper_keys}")
# Add prefix to keys
prefixed = {f"user_{k}": v for k, v in data.items()}
print(f"Prefixed: {prefixed}")
print("\n=== Transform Both ===")
raw_scores = {"alice": "85", "bob": "92", "charlie": "78"}
print(f"Raw: {raw_scores}")
# Capitalize names, convert scores to int
clean_scores = {name.capitalize(): int(score) for name, score in raw_scores.items()}
print(f"Clean: {clean_scores}")
print("\n=== Conditional Transform ===")
numbers = {"a": -3, "b": 5, "c": -1, "d": 8}
print(f"Numbers: {numbers}")
# Absolute values
absolute = {k: abs(v) for k, v in numbers.items()}
print(f"Absolute: {absolute}")
# Conditional: double positives, negate negatives
transformed = {k: v * 2 if v > 0 else -v for k, v in numbers.items()}
print(f"Transformed: {transformed}")
print("\n=== Computed Keys ===")
items = ["apple", "banana", "cherry"]
# Length as key
by_length = {len(item): item for item in items}
print(f"By length: {by_length}")
# First letter as key
by_first = {item[0]: item for item in items}
print(f"By first letter: {by_first}")
# Note: duplicates overwrite!
Apply functions to keys or values: {k.upper(): v*2 for k, v in items}.
Invert a dictionary
Swap keys and values.
# Inverting Dictionaries
print("=== Inverting Dictionary (Swap Keys and Values) ===\n")
# Original dictionary
fruit_to_color = {"apple": "red", "banana": "yellow", "grape": "purple"}
print(f"Original: {fruit_to_color}")
# Invert: color → fruit
color_to_fruit = {v: k for k, v in fruit_to_color.items()}
print(f"Inverted: {color_to_fruit}")
print("\n=== Warning: Duplicate Values ===")
# Dict with duplicate values
grades = {"Alice": "A", "Bob": "B", "Charlie": "A", "Diana": "B"}
print(f"Grades: {grades}")
# Simple invert loses data!
grade_to_student = {v: k for k, v in grades.items()}
print(f"Inverted (data lost!): {grade_to_student}")
print("\n=== Solution: Group by Value ===")
# Collect all keys with same value
from collections import defaultdict
grade_groups = defaultdict(list)
for name, grade in grades.items():
grade_groups[grade].append(name)
print(f"Grouped: {dict(grade_groups)}")
# Comprehension version (more complex)
unique_grades = set(grades.values())
grade_to_students = {
grade: [name for name, g in grades.items() if g == grade]
for grade in unique_grades
}
print(f"Comprehension: {grade_to_students}")
print("\n=== Bidirectional Lookup ===")
# Create both directions
country_code = {"USA": 1, "UK": 44, "Japan": 81}
code_country = {v: k for k, v in country_code.items()}
print(f"Country → Code: {country_code}")
print(f"Code → Country: {code_country}")
# Lookup both ways
country = "Japan"
print(f"\n{country} code: {country_code[country]}")
code = 44
print(f"Code {code}: {code_country[code]}")
{v: k for k, v in d.items()} swaps keys and values.
Exercise: practical.py
Real-world dictionary comprehension patterns