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

To work with data in Python, you need dictionaries. These structures use key-value pairs for efficient data retrieval. Create them with curly braces {} or the dict() function.
You will explore several creation methods with practical examples. You will also find implementation tips, see real-world uses, and receive advice to debug your code effectively.
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 curly brace syntax is the most idiomatic way to create a dictionary when you know the key-value pairs ahead of time. It’s both concise and highly readable. The person dictionary is initialized with three pairs, where each key is a string that maps to a value:
- The key
"name"points to the string"Alice". - The key
"age"points to the integer30. - The key
"city"points to the string"New York".
This method makes your code's intent obvious, which is why it's preferred for defining static data structures.
Different initialization methods
If you don't know your key-value pairs upfront, Python provides flexible alternatives to curly braces, including the dict() constructor, sequences, and dictionary comprehensions.
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 offers a functional way to build your dictionary. You pass key-value pairs as keyword arguments, like name="Alice". Python automatically converts the argument name (name) into a string key, which can make your code feel cleaner.
- The keys are unquoted identifiers, not string literals.
- This method is especially handy when creating a dictionary from 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 a sequence of key-value pairs, such as a list of tuples. When you pass an iterable like items to the dict() constructor, it efficiently unpacks each pair to build the dictionary.
- The first element of each tuple becomes the key.
- The second element becomes its corresponding value.
This approach is especially powerful when you're working with data that's generated dynamically, like records fetched from a database or read from a file.
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 give you a powerful, one-line syntax for creating dictionaries from iterables. In this example, the zip() function pairs each name from the names list with its corresponding score from the scores list. The comprehension then loops through these pairs to build the final dictionary.
- The expression
name: scoredefines how each key and value are assigned. - This method is highly efficient for transforming data from lists or other iterables into a dictionary.
Advanced dictionary techniques
With the fundamentals covered, you can now tackle more complex data structures using specialized methods like fromkeys() and defaultdict or by creating nested dictionaries.
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 a quick way to create a dictionary when all keys should share the same initial value. You simply provide an iterable of keys and a single default value. This is especially useful for initializing a dictionary with a predefined structure before populating it with real data.
- It efficiently maps every item from your
keysiterable to the specifieddefault_value. - If you omit the default value, each key will be assigned a value of
None.
Creating nested dictionaries
employees = {
"Alice": {"department": "Engineering", "salary": 85000},
"Bob": {"department": "Marketing", "salary": 75000}
}
print(employees["Alice"]["department"])--OUTPUT--Engineering
Nested dictionaries allow you to store complex, hierarchical data by placing dictionaries inside other dictionaries. In the employees example, each top-level key (like "Alice") maps to a value that is itself a dictionary containing that employee's specific details.
- This structure is perfect for representing objects with multiple attributes, such as user profiles or configuration settings.
- To access nested data, you chain key lookups. The expression
employees["Alice"]["department"]first retrieves the dictionary for"Alice"and then accesses the value associated with the"department"key 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 is a lifesaver when you're grouping items. It works just like a regular dictionary, but with a twist—if you try to access a key that doesn't exist, it automatically creates a default value for you instead of raising a KeyError.
- Here,
defaultdict(list)sets the factory function tolist. When you first accessgrades["Alice"], it creates an empty list for that key. - This lets you immediately
.append()values without needing to initialize the list first, making your code cleaner and more direct.
Move faster with Replit
Replit is an AI-powered development platform that transforms natural language into working applications. With Replit Agent, you can describe what you want to build, and it will create a complete app—including databases, APIs, and deployment—directly from your instructions.
The dictionary techniques you've learned are the building blocks for complex software. Replit Agent can take these concepts and turn them into production-ready tools.
- Build a user profile system that uses nested dictionaries to store complex data like settings and contact information.
- Create a log processor that leverages
defaultdictto automatically group and count events by type. - Deploy a data mapping utility that transforms spreadsheet columns into a structured dictionary using comprehensions.
Describe your app idea and let Replit Agent write, test, and deploy the code for you, all within your browser.
Common errors and challenges
Even with their flexibility, dictionaries can present common challenges, including missing keys, mutable defaults, and unsafe iteration.
Handling missing keys with the .get() method
Trying to access a dictionary key that doesn't exist with square brackets [] will crash your program with a KeyError. It's a frequent bug, but thankfully, it's easy to avoid. The following code demonstrates this exact problem in action.
user_data = {"name": "Alice", "age": 30}
email = user_data["email"] # KeyError: 'email'
print(f"User email: {email}")
The code fails because the user_data dictionary has no email key. Attempting to retrieve it directly with square brackets triggers the error. Check out the next example for a more robust way to handle this situation.
user_data = {"name": "Alice", "age": 30}
email = user_data.get("email", "No email provided")
print(f"User email: {email}")
The get() method is your safeguard against a KeyError. Instead of crashing, it lets you handle missing keys gracefully.
- It searches for a key, like
"email", and returns its value if found. - If the key is missing, it returns a default value you specify—in this case,
"No email provided".
This makes your code more robust, especially when working with unpredictable data from external sources like APIs where certain fields might be optional.
Avoiding mutable default parameter pitfall with dictionaries
Using a mutable object like a dictionary as a default function argument is a classic Python trap. The default dictionary is created only once, so it persists across multiple function calls, leading to shared state and unexpected side effects.
The following code demonstrates this problem with the add_score function, where a default leaderboard={} parameter behaves in a surprising way.
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!
Each call to add_score modifies the same default leaderboard dictionary. This is why the second call includes Alice's data instead of starting fresh. The corrected implementation below shows the proper approach.
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 corrected approach sets the default parameter to None. Inside the function, a check if leaderboard is None: creates a new dictionary for each call. This prevents the shared state that caused the bug, ensuring add_score operates on a fresh leaderboard every time.
- This pattern is crucial when using any mutable type—like a list or dictionary—as a default argument.
- It guarantees your function behaves predictably without unintended side effects.
Safely modifying dictionaries during iteration
Modifying a dictionary while you're iterating over it is a common mistake that triggers a RuntimeError. Python can't handle changes to a dictionary's size during a loop, as this disrupts the iteration process. The following code demonstrates this exact problem.
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 loop iterates over data while the del data[key] statement attempts to remove items from it. This conflict confuses the iterator, causing the RuntimeError. The next example shows how to handle this safely.
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 approach is to separate the identification and deletion steps. First, you create a new list of keys to remove, like keys_to_remove, by iterating over the dictionary's items. Then, you loop over this new list to safely delete the keys from the original dictionary.
- This two-step process prevents a
RuntimeErrorbecause you aren't modifying the dictionary while iterating through it. It's a crucial pattern for any dynamic filtering task.
Real-world applications
Now that you can sidestep common dictionary pitfalls, you can use them to solve practical problems like counting word frequencies and optimizing functions.
Counting word frequencies using .get() method
The get() method provides an elegant way to tally items, such as counting how many times each word appears in a given text.
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 by first normalizing the text. It uses .lower() to treat uppercase and lowercase words as identical, and .split() to break the string into a list of words. The code then iterates through this list.
- For each word,
word_count.get(word, 0)looks up its current count. If the word is new, it returns a default value of0. - The code then adds
1to the retrieved count and updates the dictionary with the new total.
This pattern lets you initialize and increment counts in a single, readable line.
Building a memoization cache for the fibonacci() function
You can significantly optimize slow recursive functions, like the classic fibonacci() example, by using a dictionary as a memoization cache to store and retrieve previously computed values.
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 its cache dictionary to remember results it has already calculated. Before doing any work, it checks if the value for n is already in the cache. If so, it returns the stored value immediately, skipping the expensive recursive calls.
- This technique avoids re-calculating the same Fibonacci numbers multiple times.
- If a result isn't cached, the function computes it and saves it with
cache[n] = ...before returning. - This makes future lookups for that number instantaneous, which is why the second call is so much faster.
Get started with Replit
Turn your knowledge into a real tool. Tell Replit Agent to “build a word frequency counter from a URL” or “create a tool that groups log entries by error type.”
Replit Agent will write the code, test for errors, and deploy your app for you. Start building with Replit.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.
Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.



%2520in%2520Python.png)