How to create a dictionary in Python
Learn how to create a Python dictionary with various methods. Discover tips, real-world applications, and how to debug common errors.

Python dictionaries are a fundamental data structure for developers. They store data in key-value pairs. This structure provides a flexible way to manage complex information and streamline your code.
In this article, you'll explore several techniques to create dictionaries. You'll also find practical tips, real-world applications, and debugging advice to help you use them effectively in your projects.
Creating a dictionary with curly braces
person = {"name": "Alice", "age": 30, "city": "New York"}
print(person)--OUTPUT--{'name': 'Alice', 'age': 30, 'city': 'New York'}
The most straightforward way to create a dictionary is by using curly braces {}. This method, known as a dictionary literal, is perfect for defining dictionaries when you already know the data you want to include. In the example, the person dictionary is initialized with three key-value pairs, clearly laying out its structure from the start.
Each key, like "name", is a unique identifier mapped to a specific value, such as "Alice". This direct approach is highly readable and efficient, making it a go-to for configuration settings or representing simple data objects within your code.
Different initialization methods
While curly braces are great for known values, you can also use the dict() constructor and other methods to build dictionaries more dynamically.
Using the dict() constructor
person = dict(name="Alice", age=30, city="New York")
print(person)--OUTPUT--{'name': 'Alice', 'age': 30, 'city': 'New York'}
The dict() constructor provides an alternative way to initialize a dictionary. Notice how the keys are passed as keyword arguments, like name="Alice", rather than as strings in quotes. This approach often results in cleaner-looking code, especially when your keys are simple words.
- Keys are written directly without quotes.
- Values are assigned with an equals sign (
=) instead of a colon.
It’s a great choice when you prefer a function-call style or when you're creating a dictionary dynamically from existing variables.
Creating dictionaries from sequences
items = [("name", "Alice"), ("age", 30), ("city", "New York")]
person = dict(items)
print(person)--OUTPUT--{'name': 'Alice', 'age': 30, 'city': 'New York'}
You can also build a dictionary from any sequence of key-value pairs. In the example, items is a list where each tuple contains a key and its corresponding value. When you pass this list to the dict() constructor, it neatly transforms the sequence into a dictionary.
- This approach is especially useful when your data is generated dynamically, like from a database query or file processing.
- Each item in the sequence must have exactly two elements: the key and the value.
Using dictionary comprehensions
names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 92]
grade_dict = {name: score for name, score in zip(names, scores)}
print(grade_dict)--OUTPUT--{'Alice': 95, 'Bob': 87, 'Charlie': 92}
Dictionary comprehensions offer a compact and readable way to create dictionaries from existing iterables. In this example, the zip() function first pairs up corresponding elements from the names and scores lists, creating a sequence of tuples.
- The expression
{name: score for name, score in zip(names, scores)}then iterates through these pairs. - For each pair, it assigns the
nameas the key and thescoreas the value, building the new dictionary on the fly. This is often more efficient than using a traditional loop.
Advanced dictionary techniques
Building on these foundational methods, you can create dictionaries for specific needs using tools like fromkeys(), nested dictionaries, or the convenient defaultdict.
Creating dictionaries with fromkeys() method
keys = ["name", "age", "city"]
default_value = "Unknown"
person = dict.fromkeys(keys, default_value)
print(person)--OUTPUT--{'name': 'Unknown', 'age': 'Unknown', 'city': 'Unknown'}
The fromkeys() method is your go-to for creating a dictionary where multiple keys share the same initial value. As shown in the example, it takes an iterable of keys and a single default value, then efficiently builds a new dictionary from them.
- It’s perfect for initializing a dictionary with placeholder data before you have the final values.
- If you omit the second argument, all keys will be assigned the value
Noneby default.
Creating nested dictionaries
employees = {
"Alice": {"department": "Engineering", "salary": 85000},
"Bob": {"department": "Marketing", "salary": 75000}
}
print(employees["Alice"]["department"])--OUTPUT--Engineering
Nested dictionaries let you store a dictionary as a value inside another dictionary, which is perfect for modeling hierarchical data. In the employees example, each key like "Alice" maps to another dictionary containing that employee's specific details, such as their department and salary.
- This structure helps organize complex information, like user profiles or configuration settings.
- You access nested values by chaining keys. For instance,
employees["Alice"]["department"]first finds the "Alice" record, then retrieves the "department" value from within it.
Using defaultdict for automatic initialization
from collections import defaultdict
grades = defaultdict(list)
grades["Alice"].append(95)
grades["Alice"].append(92)
print(dict(grades))--OUTPUT--{'Alice': [95, 92]}
The defaultdict from the collections module simplifies grouping data by automatically handling missing keys. When you try to access or modify a key that doesn't exist, it creates a default value for you instead of raising a KeyError.
- In this example,
defaultdict(list)ensures that any new key is automatically assigned an empty list. - This allows you to directly use methods like
append()on a new key, such asgrades["Alice"], without needing to initialize it first.
Move faster with Replit
Replit is an AI-powered development platform that comes with all Python dependencies pre-installed, so you can skip setup and start coding instantly. This lets you move from learning individual techniques to building complete applications much faster.
With Agent 4, you can describe the app you want to build, and it will handle everything from writing the code to connecting databases and deploying it. Instead of manually piecing together methods, you can focus on the final product:
- A configuration manager that uses nested dictionaries to store settings for different environments.
- An inventory tracker that leverages
defaultdictto automatically group items by category as they are added. - A data mapping tool that combines product IDs and prices from separate lists into a single lookup dictionary using
zip().
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
Even experienced developers can run into a few common pitfalls when working with dictionaries, but they're easy to avoid with the right techniques.
Handling missing keys with the .get() method
A frequent stumbling block is the KeyError, which appears when you try to access a key that doesn't exist in the dictionary. This error can crash your script unexpectedly. The code below shows exactly what happens when this occurs.
user_data = {"name": "Alice", "age": 30}
email = user_data["email"] # KeyError: 'email'
print(f"User email: {email}")
The script halts because the key "email" isn't in the user_data dictionary. Direct access like this is risky. The following example shows a safer way to retrieve values without causing an error.
user_data = {"name": "Alice", "age": 30}
email = user_data.get("email", "No email provided")
print(f"User email: {email}")
Instead of crashing, the .get() method gracefully handles missing keys. It looks for the "email" key, and since it's not there, it returns the default value "No email provided". This is a lifesaver when you're dealing with unpredictable data, like from an external API or user forms, where you can't guarantee every key will be present. Your program keeps running smoothly.
Avoiding mutable default parameter pitfall with dictionaries
It's a common trap to use a dictionary as a default argument in a function. Because dictionaries are mutable, the default object is created only once, leading to unexpected shared state between calls. The code below shows this surprising bug in action.
def add_score(name, score, leaderboard={}):
leaderboard[name] = score
return leaderboard
print(add_score("Alice", 95))
print(add_score("Bob", 87)) # Still contains Alice's score!
The add_score() function reuses the same leaderboard dictionary for every call. This causes subsequent calls to build on the previous ones, creating unintended side effects. The corrected version below shows how to prevent this behavior.
def add_score(name, score, leaderboard=None):
if leaderboard is None:
leaderboard = {}
leaderboard[name] = score
return leaderboard
print(add_score("Alice", 95))
print(add_score("Bob", 87)) # Fresh dictionary each time
The fix is to set the default argument to None and create a new dictionary inside the function. By checking if leaderboard is None: and then initializing leaderboard = {}, you guarantee that each call to add_score() works with a clean slate. This simple pattern prevents shared state between function calls, so you don't get surprising results. Keep an eye out for this whenever you use mutable types like lists or dictionaries as default parameters.
Safely modifying dictionaries during iteration
Modifying a dictionary while you're looping over it is a classic Python pitfall. It can lead to a RuntimeError because the dictionary's size changes mid-iteration, which confuses the loop. The following code demonstrates exactly what happens when you try.
data = {"a": 1, "b": 2, "c": 3, "d": 4}
for key in data:
if data[key] % 2 == 0: # Remove even values
del data[key] # RuntimeError: dictionary changed during iteration
The for loop iterates over a live view of the dictionary. When you use del data[key], you alter the dictionary's structure mid-loop, which Python prohibits. See how to safely accomplish this in the next example.
data = {"a": 1, "b": 2, "c": 3, "d": 4}
keys_to_remove = [k for k, v in data.items() if v % 2 == 0]
for key in keys_to_remove:
del data[key]
print(data) # {'a': 1, 'c': 3}
The safe way to modify a dictionary during iteration is to first identify the keys you want to change and store them in a separate list. In the example, a list comprehension creates keys_to_remove. Then, you loop over this new list to delete the keys from the original dictionary. This approach avoids the RuntimeError because you're not altering the dictionary while iterating through it, ensuring your loop completes without issues.
Real-world applications
With those common challenges solved, you can now use dictionaries for practical applications like counting word frequencies and optimizing function performance.
Counting word frequencies using .get() method
The .get() method provides a concise way to count items, like words in a text, by fetching the current count or starting from zero if the word is new.
text = "the quick brown fox jumps over the lazy dog"
word_count = {}
for word in text.lower().split():
word_count[word] = word_count.get(word, 0) + 1
print(word_count)
This snippet tallies word occurrences in a string. It first standardizes the text by making it lowercase and splitting it into a list of words. The code then iterates through this list to build a frequency map in the word_count dictionary.
- For each word,
word_count.get(word, 0)safely looks up its count. This avoids an error if the word hasn't been seen yet. - The
+ 1then increments the count, so each word's value in the dictionary reflects how many times it has appeared in the text.
Building a memoization cache for the fibonacci() function
You can also use a dictionary to build a memoization cache, which dramatically speeds up recursive functions like fibonacci() by storing results and avoiding redundant calculations.
def fibonacci_with_cache(n, cache={}):
if n in cache:
return cache[n]
if n <= 1:
return n
cache[n] = fibonacci_with_cache(n-1) + fibonacci_with_cache(n-2)
return cache[n]
print(fibonacci_with_cache(10))
print(fibonacci_with_cache(20)) # Much faster with caching
The fibonacci_with_cache function uses a dictionary to remember past results. When you call it, the function first checks if the number n is already a key in the cache. If it is, the function returns the stored value instantly, which avoids redundant calculations.
- If the result isn't in the cache, the function computes it recursively.
- It then saves the new result in the
cachedictionary before returning it.
This technique ensures that each Fibonacci number is computed only once, making the process significantly faster for larger numbers - especially useful when building optimization solutions with AI coding.
Get started with Replit
Now, turn your knowledge into a working tool. Describe what you want to build, like a "word frequency counter using a dictionary" or a "config manager with nested dictionaries for different environments."
Replit Agent will write the code, test for errors, and deploy your application for you. Start building with Replit.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.



