How to Create a Dynamic Dictionary in Python
Building dictionaries at runtime is essential for data processing, configuration management, and working with dynamic data structures. Rather than hardcoding key-value pairs, you often need to construct dictionaries programmatically from user input, database results, API responses, or computed values.
This guide walks you through multiple approaches for creating dynamic dictionaries in Python, from merging parallel lists with zip() to grouping data with defaultdict. Each method is explained with clear examples and output so you can choose the right tool for your specific use case.
Using zip() to Merge Parallel Lists
The most common way to build a dictionary from two separate sequences is to combine them with zip() and pass the result to the dict() constructor:
keys = ["id", "name", "email"]
values = [101, "Alice", "alice@example.com"]
user = dict(zip(keys, values))
print(user)
Output:
{'id': 101, 'name': 'Alice', 'email': 'alice@example.com'}
zip() pairs each key with its corresponding value by position, and dict() converts those pairs into a dictionary.
Handling Unequal List Lengths
When the lists have different lengths, zip() silently stops at the shortest one:
keys = ["a", "b", "c", "d"]
values = [1, 2]
result = dict(zip(keys, values))
print(result)
Output:
{'a': 1, 'b': 2}
Keys "c" and "d" are discarded without any warning. This can lead to subtle data loss bugs if you are not expecting it.
zip_longestUse itertools.zip_longest to include all elements from both lists, filling missing values with a default:
from itertools import zip_longest
keys = ["a", "b", "c", "d"]
values = [1, 2]
result = dict(zip_longest(keys, values, fillvalue=None))
print(result)
Output:
{'a': 1, 'b': 2, 'c': None, 'd': None}
Using Dictionary Comprehension
Dictionary comprehensions let you create dictionaries with transformations, computations, or filtering logic applied inline:
# Squares of numbers
squares = {x: x ** 2 for x in range(6)}
print(squares)
Output:
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
You can add a condition to filter which entries are included:
# Only even numbers
even_squares = {x: x ** 2 for x in range(10) if x % 2 == 0}
print(even_squares)
Output:
{0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
Transforming Existing Data
Dictionary comprehensions are especially useful for converting one data structure into another:
words = ["apple", "banana", "cherry"]
# Map each word to its length
lengths = {word: len(word) for word in words}
print(lengths)
# Map each index to its word
indexed = {i: word for i, word in enumerate(words)}
print(indexed)
Output:
{'apple': 5, 'banana': 6, 'cherry': 6}
{0: 'apple', 1: 'banana', 2: 'cherry'}
Building from Tuples or Key-Value Pairs
The dict() constructor directly accepts any iterable of two-element sequences:
pairs = [("name", "Alice"), ("age", 30), ("city", "Boston")]
user = dict(pairs)
print(user)
Output:
{'name': 'Alice', 'age': 30, 'city': 'Boston'}
This is particularly useful when your data arrives as a list of tuples from a database query, a CSV reader, or a function that yields key-value pairs.
Using fromkeys() for Uniform Default Values
When you need a dictionary with predefined keys that all start with the same value, dict.fromkeys() provides a concise way to create one:
keys = ["read", "write", "execute"]
permissions = dict.fromkeys(keys, False)
print(permissions)
Output:
{'read': False, 'write': False, 'execute': False}
A Common Mistake: Mutable Default Values with fromkeys()
A frequent and confusing bug occurs when you use a mutable object like a list as the default value:
bad = dict.fromkeys(["a", "b"], [])
bad["a"].append(1)
print(bad)
Output:
{'a': [1], 'b': [1]}
Both keys share the same list object, so appending to one affects the other. To give each key its own independent list, use a dictionary comprehension instead:
good = {key: [] for key in ["a", "b"]}
good["a"].append(1)
print(good)
Output:
{'a': [1], 'b': []}
This fromkeys() pitfall applies to any mutable default value, including lists, dictionaries, and sets. Always use a comprehension when each key needs its own independent mutable object.
Using defaultdict for Grouping and Counting
When you are building a dictionary dynamically and do not know the keys in advance, defaultdict from the collections module eliminates the need for key-existence checks:
Counting Occurrences
from collections import defaultdict
items = ["apple", "banana", "apple", "cherry", "banana", "apple"]
counts = defaultdict(int)
for item in items:
counts[item] += 1
print(dict(counts))
Output:
{'apple': 3, 'banana': 2, 'cherry': 1}
When a missing key is accessed, defaultdict(int) automatically initializes it with 0, so += 1 works on the first encounter without raising a KeyError.
Grouping Items by a Property
from collections import defaultdict
users = [
{"name": "Alice", "dept": "Engineering"},
{"name": "Bob", "dept": "Sales"},
{"name": "Carol", "dept": "Engineering"},
{"name": "Dave", "dept": "Sales"}
]
by_department = defaultdict(list)
for user in users:
by_department[user["dept"]].append(user["name"])
print(dict(by_department))
Output:
{'Engineering': ['Alice', 'Carol'], 'Sales': ['Bob', 'Dave']}
Each missing key is automatically initialized with an empty list, so you can call .append() directly without checking whether the key exists first.
Using setdefault() for Conditional Initialization
The setdefault() method provides similar functionality to defaultdict but works on regular dictionaries. It sets a key to a default value only if the key does not already exist:
data = {}
items = [("fruits", "apple"), ("vegetables", "carrot"), ("fruits", "banana")]
for category, item in items:
data.setdefault(category, []).append(item)
print(data)
Output:
{'fruits': ['apple', 'banana'], 'vegetables': ['carrot']}
The first time "fruits" is encountered, setdefault creates the key with an empty list. On subsequent encounters, it returns the existing list, and .append() adds the new item to it.
Merging Multiple Dictionaries
When you need to combine dictionaries from different sources, Python offers several approaches depending on your version:
defaults = {"theme": "light", "language": "en"}
user_prefs = {"theme": "dark"}
# Python 3.9+ merge operator
config = defaults | user_prefs
print(config)
Output:
{'theme': 'dark', 'language': 'en'}
For earlier Python versions, use the unpacking syntax:
config = {**defaults, **user_prefs}
print(config)
Output:
{'theme': 'dark', 'language': 'en'}
In both cases, values from the rightmost dictionary take precedence when keys overlap. The "theme" key from user_prefs overrides the one from defaults.
The | merge operator creates a new dictionary. If you want to update an existing dictionary in place, use |= or the .update() method:
defaults |= user_prefs # Python 3.9+
defaults.update(user_prefs) # All Python 3 versions
Method Comparison
| Method | Syntax | Best For |
|---|---|---|
dict(zip()) | dict(zip(keys, values)) | Merging parallel lists |
| Comprehension | {k: v for k in iterable} | Transformations, filtering |
dict(pairs) | dict([(k1, v1), ...]) | Converting key-value tuples |
fromkeys() | dict.fromkeys(keys, val) | Uniform immutable defaults |
defaultdict | defaultdict(type) | Grouping, counting, aggregation |
setdefault() | d.setdefault(k, default) | Conditional initialization |
Merge (| or **) | d1 | d2 or {**d1, **d2} | Combining multiple dictionaries |
Conclusion
Python provides a rich set of tools for building dictionaries dynamically.
- Use
dict(zip())when you have separate sequences of keys and values that need to be paired together. Choose dictionary comprehensions when you need to transform, compute, or filter data during construction. - Reach for
defaultdictwhen aggregating data with unknown keys, such as counting occurrences or grouping items. Usesetdefault()for conditional initialization on regular dictionaries. - And apply merge operators or unpacking when combining multiple dictionaries into one.
By selecting the right method for each situation, you can write cleaner, more efficient, and more Pythonic code for any dynamic dictionary construction task.