Python - Iterate Over Dictionary (keys, values, items)
The most straightforward iteration pattern accesses only the dictionary keys. Python provides multiple syntactic approaches, though they differ in explicitness and compatibility.
Key Insights
- Python dictionaries offer three primary iteration methods:
keys(),values(), anditems(), each optimized for different access patterns and use cases - Dictionary views returned by these methods are dynamic, memory-efficient objects that reflect real-time changes to the underlying dictionary without creating copies
- Understanding iteration performance characteristics and dictionary mutability constraints during iteration prevents common bugs and enables efficient data processing
Iterating Over Dictionary Keys
The most straightforward iteration pattern accesses only the dictionary keys. Python provides multiple syntactic approaches, though they differ in explicitness and compatibility.
user_permissions = {
'admin': 'full_access',
'editor': 'write_access',
'viewer': 'read_access'
}
# Implicit iteration (default behavior)
for role in user_permissions:
print(f"Role: {role}")
# Explicit iteration using keys()
for role in user_permissions.keys():
print(f"Role: {role}")
Both approaches produce identical results, but keys() makes intent explicit. The keys() method returns a dictionary view object, not a list, which means it doesn’t consume additional memory proportional to dictionary size.
# Dictionary views are dynamic
config = {'timeout': 30, 'retries': 3}
keys_view = config.keys()
print(list(keys_view)) # ['timeout', 'retries']
config['max_connections'] = 100
print(list(keys_view)) # ['timeout', 'retries', 'max_connections']
When you need a static snapshot or require list operations, convert the view explicitly:
api_endpoints = {
'/users': 'GET',
'/posts': 'GET',
'/auth': 'POST'
}
# Convert to list for indexing or slicing
endpoint_list = list(api_endpoints.keys())
print(endpoint_list[0]) # '/users'
print(endpoint_list[:2]) # ['/users', '/posts']
Iterating Over Dictionary Values
When keys are irrelevant and you need only the stored data, use values():
response_times = {
'service_a': 145,
'service_b': 89,
'service_c': 203,
'service_d': 67
}
# Calculate statistics from values
total_time = sum(response_times.values())
avg_time = total_time / len(response_times)
max_time = max(response_times.values())
print(f"Average: {avg_time}ms, Max: {max_time}ms")
The values() view is particularly useful for filtering, transformations, and aggregations:
inventory = {
'laptop': 450.00,
'mouse': 25.00,
'keyboard': 75.00,
'monitor': 300.00
}
# Find products within budget
budget = 100.00
affordable_count = sum(1 for price in inventory.values() if price <= budget)
print(f"Affordable items: {affordable_count}")
# Apply discount to all prices
discount_rate = 0.15
discounted_prices = [price * (1 - discount_rate) for price in inventory.values()]
Iterating Over Key-Value Pairs with items()
The items() method provides simultaneous access to both keys and values, eliminating the need for key-based lookups during iteration:
database_config = {
'host': 'localhost',
'port': 5432,
'database': 'production',
'user': 'app_user'
}
# Inefficient: looking up values by key
for key in database_config:
value = database_config[key] # Additional lookup operation
print(f"{key}: {value}")
# Efficient: direct access to both
for key, value in database_config.items():
print(f"{key}: {value}")
This pattern is essential for dictionary transformations and filtering:
user_scores = {
'alice': 85,
'bob': 92,
'charlie': 78,
'diana': 95,
'evan': 88
}
# Filter dictionary based on conditions
passing_scores = {name: score for name, score in user_scores.items() if score >= 80}
print(passing_scores) # {'alice': 85, 'bob': 92, 'diana': 95, 'evan': 88}
# Transform keys and values
normalized_scores = {name.upper(): score / 100 for name, score in user_scores.items()}
Performance Considerations and Best Practices
Dictionary views are memory-efficient, but understanding their characteristics optimizes performance:
import sys
large_dict = {f'key_{i}': i for i in range(10000)}
# Views don't copy data
keys_view = large_dict.keys()
keys_list = list(large_dict.keys())
print(f"View size: {sys.getsizeof(keys_view)} bytes")
print(f"List size: {sys.getsizeof(keys_list)} bytes")
# List consumes significantly more memory
For membership testing, views provide O(1) performance:
cache = {f'user_{i}': {'data': i} for i in range(1000)}
# Efficient membership test
if 'user_500' in cache.keys(): # O(1)
print("Cache hit")
# Even more efficient (implicit keys())
if 'user_500' in cache: # O(1)
print("Cache hit")
Handling Dictionary Modification During Iteration
Modifying a dictionary while iterating over it raises RuntimeError. Create a copy or collect modifications separately:
metrics = {
'cpu_usage': 45,
'memory_usage': 78,
'disk_usage': 92,
'network_usage': 23
}
# WRONG: Causes RuntimeError
# for key, value in metrics.items():
# if value > 90:
# del metrics[key]
# CORRECT: Iterate over a copy
for key, value in list(metrics.items()):
if value > 90:
del metrics[key]
print(metrics) # {'cpu_usage': 45, 'memory_usage': 78, 'network_usage': 23}
Alternative approach using dictionary comprehension:
metrics = {
'cpu_usage': 45,
'memory_usage': 78,
'disk_usage': 92,
'network_usage': 23
}
# Create new dictionary with filtered items
filtered_metrics = {k: v for k, v in metrics.items() if v <= 90}
Advanced Iteration Patterns
Combine iteration methods with built-in functions for complex operations:
from collections import defaultdict
# Group items by value ranges
scores = {'test_1': 85, 'test_2': 92, 'test_3': 78, 'test_4': 95, 'test_5': 88}
grade_groups = defaultdict(list)
for test, score in scores.items():
if score >= 90:
grade_groups['A'].append(test)
elif score >= 80:
grade_groups['B'].append(test)
else:
grade_groups['C'].append(test)
print(dict(grade_groups))
Parallel iteration over multiple dictionaries:
prices = {'apple': 0.50, 'banana': 0.30, 'orange': 0.60}
quantities = {'apple': 10, 'banana': 15, 'orange': 8}
# Calculate total cost per item
total_costs = {
item: prices[item] * quantities.get(item, 0)
for item in prices.keys()
}
print(total_costs) # {'apple': 5.0, 'banana': 4.5, 'orange': 4.8}
Sorting dictionaries during iteration:
page_views = {
'/home': 1500,
'/about': 300,
'/products': 850,
'/contact': 200
}
# Sort by keys
for page in sorted(page_views.keys()):
print(f"{page}: {page_views[page]}")
# Sort by values (descending)
for page, views in sorted(page_views.items(), key=lambda x: x[1], reverse=True):
print(f"{page}: {views}")
Choosing the Right Iteration Method
Select iteration methods based on access requirements:
- Use implicit iteration or
keys()when only keys are needed for lookups, membership tests, or key-based operations - Use
values()for aggregations, statistics, or transformations where keys are irrelevant - Use
items()when both keys and values are required, avoiding redundant lookups - Convert views to lists only when indexing, slicing, or multiple passes are necessary
These iteration patterns form the foundation of efficient dictionary processing in Python applications, from configuration management to data transformation pipelines.