How to initialize a dictionary in Python

Python dictionaries store key-value pairs that let you organize and access data efficiently. Understanding how to properly initialize these essential data structures helps you write cleaner, more maintainable code that performs better.

This guide covers initialization techniques, practical tips, and real-world applications, with code examples created using Claude, an AI assistant built by Anthropic. You'll learn debugging strategies to avoid common pitfalls.

Creating a dictionary with curly braces {}

student = {"name": "John", "age": 21, "courses": ["Math", "Science"]}
print(student)
{'name': 'John', 'age': 21, 'courses': ['Math', 'Science']}

The curly brace syntax creates a dictionary by directly specifying key-value pairs within {} delimiters. Each pair uses a colon to separate the key from its value, while commas distinguish between different pairs. This approach provides better readability than alternative initialization methods when you know the initial values upfront.

The example demonstrates three common value types you'll encounter in Python dictionaries:

  • Simple strings like "name": "John"
  • Numbers such as "age": 21
  • Complex data structures including lists, shown in "courses": ["Math", "Science"]

This flexibility in value types makes dictionaries powerful for organizing heterogeneous data in a single structure while maintaining clear associations through meaningful keys.

Basic dictionary initialization methods

Beyond the curly brace syntax, Python offers three powerful dictionary initialization methods: the dict() constructor, dict.fromkeys(), and dictionary comprehension—each serving distinct use cases.

Using the dict() constructor

student = dict(name="John", age=21, courses=["Math", "Science"])
print(student)
{'name': 'John', 'age': 21, 'courses': ['Math', 'Science']}

The dict() constructor offers a cleaner alternative to curly brace syntax, especially when your keys are valid Python identifiers. It accepts keyword arguments where each parameter name becomes a dictionary key.

  • Keys don't need quotes when using dict() with keyword arguments
  • Values maintain their original data types, as shown by age=21 remaining an integer
  • Complex values like lists work seamlessly, demonstrated by courses=["Math", "Science"]

This method particularly shines when working with programmatically generated keys or when you want to avoid quote-heavy syntax. However, you can't use this format if your keys contain spaces or special characters. Those cases require different initialization approaches.

Using dict.fromkeys() method

keys = ["name", "age", "grade"]
student = dict.fromkeys(keys, "Unknown")
print(student)
{'name': 'Unknown', 'age': 'Unknown', 'grade': 'Unknown'}

The dict.fromkeys() method creates a dictionary by using an iterable (like a list) as keys and assigning the same value to each key. This approach excels when you need to initialize multiple dictionary entries with identical default values.

  • The first argument specifies the keys you want to create (keys = ["name", "age", "grade"])
  • The second argument sets a uniform value for all keys ("Unknown" in this case)
  • If you omit the second argument, Python automatically assigns None to all keys

This method proves particularly useful when building template dictionaries or creating placeholder structures that you'll populate with real data later. However, be cautious when using mutable objects as the default value. Python will use the same object reference for all keys.

Using dictionary comprehension

names = ["John", "Emma", "Alex"]
scores = [85, 92, 78]
student_scores = {name: score for name, score in zip(names, scores)}
print(student_scores)
{'John': 85, 'Emma': 92, 'Alex': 78}

Dictionary comprehension provides a concise way to create dictionaries by transforming and filtering data. The syntax mirrors list comprehension but uses curly braces and requires both a key and value expression.

The example combines two lists into a dictionary using zip() to pair student names with their corresponding scores. Each iteration creates a key-value pair where names become keys and scores become values.

  • The expression {name: score for name, score in zip(names, scores)} processes both lists simultaneously
  • Each name-score pair creates one dictionary entry
  • The resulting dictionary maintains the original order of the input lists

This approach particularly shines when you need to transform existing data into a dictionary format. It's more readable than traditional loops for simple transformations and often performs better.

Advanced dictionary initialization techniques

Building on these foundational techniques, Python offers advanced dictionary features like the | operator for combining dictionaries, nested structures for complex data, and defaultdict for smarter initialization patterns.

Merging dictionaries with the | operator

personal_info = {"name": "John", "age": 21}
academic_info = {"major": "Computer Science", "GPA": 3.8}
student = personal_info | academic_info  # Python 3.9+ syntax
print(student)
{'name': 'John', 'age': 21, 'major': 'Computer Science', 'GPA': 3.8}

The | operator, introduced in Python 3.9, combines two dictionaries into a single new one. This merge operator creates a clean, readable alternative to traditional dictionary merging methods.

  • The operator preserves the original dictionaries (personal_info and academic_info) without modifying them
  • Keys from the right dictionary (academic_info) override any duplicate keys from the left dictionary
  • The resulting dictionary maintains insertion order, following Python's standard dictionary behavior since version 3.7

When working with multiple data sources, this operator simplifies the process of combining related information into a unified structure. It's particularly useful for scenarios like merging user profiles, configuration settings, or database records.

Creating nested dictionaries

students = {
    "John": {"age": 21, "courses": ["Math", "Science"]},
    "Emma": {"age": 20, "courses": ["History", "English"]}
}
print(students["John"]["courses"])
['Math', 'Science']

Nested dictionaries store dictionaries as values within another dictionary. The example creates a student database where each student's name links to another dictionary containing their details.

  • Access nested values by chaining square bracket notation: students["John"]["courses"] retrieves John's course list
  • Each inner dictionary can have different structures. This flexibility helps organize complex hierarchical data
  • Inner dictionaries follow the same rules as regular dictionaries. They can contain any valid Python data type as values

This structure works well for representing real-world relationships where objects have multiple related attributes. Common applications include user profiles, game states, and configuration settings.

Using defaultdict for automatic initialization

from collections import defaultdict
student_grades = defaultdict(list)
student_grades["John"].append(85)
student_grades["John"].append(92)
print(dict(student_grades))
{'John': [85, 92]}

defaultdict automatically creates a default value when you access a non-existent key. In this example, specifying list as the default factory means any new key will automatically initialize with an empty list. This eliminates the need to check if a key exists before appending values.

  • When you call student_grades["John"].append(85), Python creates an empty list for "John" if it doesn't exist then adds the grade
  • This approach prevents KeyError exceptions that would occur with regular dictionaries
  • The dict(student_grades) conversion shows you can treat defaultdict like a normal dictionary when needed

This pattern particularly shines when collecting multiple values per key. It streamlines code by removing repetitive dictionary initialization checks.

Creating a frequency counter with dict.get()

The dict.get() method enables efficient word frequency tracking by safely retrieving existing counts while providing a default value for new words—making it perfect for analyzing text patterns without explicit key checking.

text = "apple banana apple orange banana apple"
word_count = {}
for word in text.split():
    word_count[word] = word_count.get(word, 0) + 1
print(word_count)

This code efficiently counts how many times each word appears in a text string. The split() method breaks the text into individual words. For each word, the code uses get() to either retrieve its existing count from the dictionary or return 0 if the word isn't found yet.

The clever part is how it handles both new and existing words in a single line. When processing "apple" for the first time, get() returns 0. The code adds 1 to create the initial count. On subsequent appearances, it adds 1 to the current count.

  • Eliminates the need for separate checks on whether a word exists
  • Handles dictionary updates in a clean, concise way
  • Creates an elegant solution for counting occurrences

Implementing a simple memoization cache with dictionaries

Dictionaries serve as efficient caching tools to store and retrieve previously calculated results, preventing redundant expensive computations by saving the output values for quick lookup on subsequent function calls.

cache = {}

def calculate_expensive_value(x):
    if x in cache:
        return f"From cache: {cache[x]}"
    result = x * x * x  # Simulate expensive calculation
    cache[x] = result
    return f"Calculated: {result}"

print(calculate_expensive_value(5))
print(calculate_expensive_value(5))  # Second call uses cache

The code demonstrates a practical caching technique that stores computed values in a dictionary to avoid redundant calculations. When calculate_expensive_value() runs, it first checks if the input exists in the cache dictionary. If found, it immediately returns the cached result with a "From cache" message.

For new inputs, the function performs the calculation (x * x * x), stores the result in the cache, and returns it with a "Calculated" message. This pattern becomes especially valuable when dealing with computationally intensive operations.

  • First call with input 5 performs the calculation
  • Second call retrieves the pre-computed result
  • Dictionary lookup is significantly faster than recalculating values

Common errors and challenges

Understanding these common Python dictionary pitfalls helps you write more reliable code by preventing key errors, iteration issues, and type-related problems.

Avoiding KeyError when accessing non-existent dictionary keys

Accessing a non-existent dictionary key triggers Python's KeyError exception, disrupting your program's flow. This common issue often catches new developers off guard when retrieving values without first verifying the key's existence. The following code demonstrates this error in action.

student_scores = {"John": 85, "Emma": 92}
print(student_scores["Alex"])  # This will raise KeyError: 'Alex'

The code attempts to directly access a value using a key that doesn't exist in the dictionary. Python can't find "Alex" in student_scores, so it raises an error instead of returning an empty or default value.

The next code example shows how to properly handle missing dictionary keys.

student_scores = {"John": 85, "Emma": 92}
print(student_scores.get("Alex", "Not found"))

The get() method provides a safer way to access dictionary values by accepting a default return value as its second argument. When Python can't find the requested key, it returns this fallback instead of raising an error.

  • Use get() when you're unsure if a key exists
  • The default value helps maintain program flow without extra error handling
  • Common in data processing where missing values are expected

Watch for this pattern when working with user input, API responses, or any data source where keys might be missing. The get() method transforms potential errors into graceful handling of edge cases.

Avoiding errors when modifying a dictionary during iteration

Modifying a dictionary while iterating through it can trigger a RuntimeError. Python raises this error to prevent unpredictable behavior when you add or remove items during a for loop. The following code demonstrates this common pitfall when trying to remove low scores from a dictionary.

scores = {"John": 65, "Emma": 45, "Alex": 90}
for name, score in scores.items():
    if score < 50:
        del scores[name]  # RuntimeError: dictionary changed size during iteration
print(scores)

The error occurs because Python's dictionary iterator can't track its position when you delete items mid-loop. The size change disrupts the iteration sequence, causing unpredictable results. Let's examine a safer approach in the code below.

scores = {"John": 65, "Emma": 45, "Alex": 90}
passing_scores = {name: score for name, score in scores.items() if score >= 50}
print(passing_scores)

Dictionary comprehension offers a safer alternative to modifying dictionaries during iteration. The expression {name: score for name, score in scores.items() if score >= 50} creates a new dictionary containing only passing scores, avoiding the RuntimeError that occurs when deleting items from the original dictionary.

  • Watch for this error when filtering, updating, or removing dictionary items in loops
  • Create a new dictionary instead of modifying the existing one during iteration
  • Consider using list comprehension to collect keys first if you must modify the original dictionary

This pattern proves especially useful when processing data streams or cleaning datasets where you need to filter out unwanted key-value pairs based on specific conditions.

Using immutable types as dictionary keys

Python dictionaries require immutable keys that won't change after creation. Lists and other mutable objects can't serve as dictionary keys since their contents might change, disrupting the dictionary's internal organization. The code below demonstrates what happens when you attempt to use a list as a key.

student_grades = {}
courses = ["Math", "Science"]
student_grades[courses] = [85, 92]  # TypeError: unhashable type: 'list'
print(student_grades)

Python's dictionary system requires keys that can be converted into unique hash values. Lists change after creation, making them incompatible with this hashing requirement. The code below demonstrates the proper approach using an immutable alternative.

student_grades = {}
courses = ("Math", "Science")  # Using immutable tuple instead of list
student_grades[courses] = [85, 92]
print(student_grades)

Converting the list to a tuple solves the "unhashable type" error because tuples are immutable. Python can generate a consistent hash value for tuples since their contents won't change after creation. This makes tuples suitable as dictionary keys while maintaining the same sequence functionality.

Watch for this error when using compound data types as dictionary keys. Common scenarios include:

  • Storing coordinates as keys (x, y)
  • Using date ranges (start_date, end_date)
  • Creating composite keys from multiple values

Remember that strings, numbers, and tuples work as dictionary keys. Lists, sets, and dictionaries don't.

FAQs

What is the difference between using curly braces and the dict() function to create a dictionary?

Both {} and dict() create Python dictionaries, but they serve different use cases. Curly braces offer a cleaner syntax for creating dictionaries with known key-value pairs. The dict() function provides more flexibility when working with dynamic data structures or converting other collection types.

  • Curly braces work best for literal dictionary creation: {'name': 'Alice'}
  • The dict() constructor excels at creating dictionaries from sequences, keyword arguments, or generator expressions

Can you create a dictionary with mixed data types as values?

Yes, Python dictionaries can store multiple data types as values in a single dictionary. The language's dynamic typing allows you to mix integers, strings, lists, or even other dictionaries as values while using any immutable type as keys.

A dictionary maps each unique key to exactly one value, creating a flexible data structure for real-world modeling. For example, a user profile dictionary might store a numeric ID, string name, and list of interests all together: user = {"id": 42, "name": "Alice", "interests": ["coding", "art"]}.

How do you initialize a dictionary with default values for specific keys?

Python's dict.fromkeys() method creates a dictionary with specified keys and a uniform default value. While you can set any default, None serves as a common choice. For more granular control, the defaultdict class from the collections module automatically handles missing keys by calling a function to generate default values.

  • Use dict.fromkeys(['key1', 'key2'], 0) for simple initialization with the same default
  • Choose defaultdict(list) when you need dynamic defaults like empty lists or integers

These approaches eliminate repetitive key-value assignments and reduce error-prone manual initialization.

What happens if you try to create a dictionary with duplicate keys?

Python dictionaries only keep the last value when you assign multiple values to the same key. This behavior stems from dictionaries' core purpose as hash tables that map unique keys to values. When Python encounters a duplicate key during dictionary creation, it simply overwrites the previous value—no error occurs.

This design enables common programming patterns like updating configuration settings or merging data from multiple sources. For example, when you write {"x": 1, "x": 2}, the dictionary will contain {"x": 2}.

Is it possible to initialize an empty dictionary and add elements later?

Yes, you can initialize an empty dictionary in Python using either {} or dict() and add key-value pairs later. This flexibility stems from Python's mutable dictionary design, which allows dynamic modifications after creation.

Adding elements uses straightforward syntax: dictionary[key] = value. The square bracket notation creates a new key-value pair if the key doesn't exist. Python's hash table implementation makes these operations efficient, typically running in O(1) time.

🏠