Python - Dictionary Comprehension

• Dictionary comprehensions provide a concise syntax for creating dictionaries from iterables, reducing multi-line loops to single expressions while maintaining readability

Key Insights

• Dictionary comprehensions provide a concise syntax for creating dictionaries from iterables, reducing multi-line loops to single expressions while maintaining readability • They support conditional logic (if/else), nested iterations, and can transform both keys and values simultaneously, making them ideal for data transformation tasks • Performance benchmarks show dictionary comprehensions are 10-30% faster than traditional loops due to optimized C-level implementation in CPython

Basic Syntax and Structure

Dictionary comprehensions follow the pattern {key_expression: value_expression for item in iterable}. This creates a new dictionary by iterating through an iterable and applying expressions to generate key-value pairs.

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

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

The comprehension evaluates the key expression first, then the value expression for each iteration. Both expressions have access to the iteration variable.

# Creating a dictionary from two lists
keys = ['name', 'age', 'city']
values = ['Alice', 30, 'NYC']

user_data = {k: v for k, v in zip(keys, values)}
# Output: {'name': 'Alice', 'age': 30, 'city': 'NYC'}

Conditional Filtering

Add conditional logic with if clauses to filter which items become dictionary entries. The condition is evaluated before creating the key-value pair.

# Filter even numbers only
even_squares = {x: x ** 2 for x in range(10) if x % 2 == 0}
# Output: {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}

# Filter based on value conditions
prices = {'apple': 0.5, 'banana': 0.3, 'orange': 0.8, 'grape': 1.2}
expensive_items = {item: price for item, price in prices.items() if price > 0.5}
# Output: {'orange': 0.8, 'grape': 1.2}

For conditional value assignment (if-else), place the condition within the value expression:

# Categorize numbers as even or odd
number_types = {x: 'even' if x % 2 == 0 else 'odd' for x in range(5)}
# Output: {0: 'even', 1: 'odd', 2: 'even', 3: 'odd', 4: 'even'}

# Apply discount to expensive items
adjusted_prices = {
    item: price * 0.9 if price > 1.0 else price 
    for item, price in prices.items()
}

Transforming Existing Dictionaries

Dictionary comprehensions excel at transforming existing dictionaries by modifying keys, values, or both.

# Convert keys to uppercase
original = {'name': 'Bob', 'age': 25, 'city': 'LA'}
uppercase_keys = {k.upper(): v for k, v in original.items()}
# Output: {'NAME': 'Bob', 'AGE': 25, 'CITY': 'LA'}

# Apply function to all values
def celsius_to_fahrenheit(c):
    return (c * 9/5) + 32

temps_c = {'morning': 15, 'noon': 22, 'evening': 18}
temps_f = {time: celsius_to_fahrenheit(temp) for time, temp in temps_c.items()}
# Output: {'morning': 59.0, 'noon': 71.6, 'evening': 64.4}

Swap keys and values when values are unique and hashable:

user_ids = {'alice': 101, 'bob': 102, 'charlie': 103}
id_to_user = {v: k for k, v in user_ids.items()}
# Output: {101: 'alice', 102: 'bob', 103: 'charlie'}

Nested Dictionary Comprehensions

Nest comprehensions to create dictionaries of dictionaries or process nested structures.

# Create multiplication table
multiplication_table = {
    i: {j: i * j for j in range(1, 6)} 
    for i in range(1, 6)
}
# Output: {1: {1: 1, 2: 2, 3: 3, 4: 4, 5: 5},
#          2: {1: 2, 2: 4, 3: 6, 4: 8, 5: 10}, ...}

# Flatten nested dictionary
nested = {
    'user1': {'name': 'Alice', 'age': 30},
    'user2': {'name': 'Bob', 'age': 25}
}

flattened = {
    f"{user_id}_{key}": value
    for user_id, user_data in nested.items()
    for key, value in user_data.items()
}
# Output: {'user1_name': 'Alice', 'user1_age': 30, 
#          'user2_name': 'Bob', 'user2_age': 25}

Practical Data Processing Examples

Dictionary comprehensions shine in real-world data transformation scenarios.

# Group items by category
transactions = [
    {'item': 'apple', 'category': 'fruit', 'amount': 5},
    {'item': 'carrot', 'category': 'vegetable', 'amount': 3},
    {'item': 'banana', 'category': 'fruit', 'amount': 7},
]

category_totals = {
    category: sum(t['amount'] for t in transactions if t['category'] == category)
    for category in set(t['category'] for t in transactions)
}
# Output: {'fruit': 12, 'vegetable': 3}

Parse configuration strings into structured data:

config_lines = [
    'database=postgres',
    'host=localhost',
    'port=5432',
    'debug=true'
]

config = {
    line.split('=')[0]: line.split('=')[1] 
    for line in config_lines
}
# Output: {'database': 'postgres', 'host': 'localhost', 
#          'port': '5432', 'debug': 'true'}

Build lookup tables for performance optimization:

# Create index for fast lookups
users = [
    {'id': 1, 'name': 'Alice', 'email': 'alice@example.com'},
    {'id': 2, 'name': 'Bob', 'email': 'bob@example.com'},
]

user_by_id = {user['id']: user for user in users}
user_by_email = {user['email']: user for user in users}

# Fast O(1) lookup instead of O(n) iteration
alice = user_by_email['alice@example.com']

Handling Complex Key-Value Expressions

Use parentheses and function calls for complex transformations:

import json

# Parse JSON strings as values
raw_data = {
    'user1': '{"name": "Alice", "score": 95}',
    'user2': '{"name": "Bob", "score": 87}'
}

parsed_data = {k: json.loads(v) for k, v in raw_data.items()}

# Complex key generation
words = ['hello', 'world', 'python']
word_analysis = {
    word: {
        'length': len(word),
        'vowels': sum(1 for c in word if c in 'aeiou'),
        'uppercase': word.upper()
    }
    for word in words
}

Performance Considerations

Dictionary comprehensions outperform equivalent loops due to optimized implementation:

import timeit

# Benchmark comparison
def using_loop():
    result = {}
    for i in range(1000):
        result[i] = i ** 2
    return result

def using_comprehension():
    return {i: i ** 2 for i in range(1000)}

loop_time = timeit.timeit(using_loop, number=10000)
comp_time = timeit.timeit(using_comprehension, number=10000)

print(f"Loop: {loop_time:.4f}s")
print(f"Comprehension: {comp_time:.4f}s")
print(f"Speedup: {loop_time/comp_time:.2f}x")
# Typical output shows 1.2-1.3x speedup

However, prioritize readability over micro-optimizations. For complex logic spanning multiple lines, traditional loops with clear variable names often improve maintainability:

# Avoid overly complex comprehensions
# BAD: Hard to read
result = {k: v for k, v in data.items() if v > threshold and k.startswith('x') if process(v)}

# GOOD: Clear intent
result = {}
for key, value in data.items():
    if value > threshold and key.startswith('x'):
        processed_value = process(value)
        if processed_value:
            result[key] = processed_value

Dictionary comprehensions are a powerful tool for concise data transformation. Use them when the logic fits naturally in a single expression, and fall back to traditional loops when complexity demands clarity.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.