How to append to a dictionary in Python

Learn how to append to a Python dictionary. Explore methods, tips, real-world applications, and how to debug common errors.

How to append to a dictionary in Python
Published on: 
Fri
Feb 6, 2026
Updated on: 
Tue
Feb 10, 2026
The Replit Team Logo Image
The Replit Team

You will often need to add new key-value pairs to a Python dictionary. This dynamic modification makes dictionaries a flexible data structure for many common programming tasks.

You will learn several techniques to update dictionaries, from simple assignment to the update() method. You'll also get tips, see real-world applications, and find advice to debug common errors.

Adding a key-value pair to a dictionary

student = {"name": "John", "age": 21}
student["grade"] = "A"
print(student)--OUTPUT--{'name': 'John', 'age': 21, 'grade': 'A'}

The most straightforward way to add a new entry to a dictionary is by using square bracket syntax. The line student["grade"] = "A" assigns the value "A" to the new key "grade", effectively expanding the dictionary.

This method is efficient because it handles two jobs at once. If the key doesn't exist, Python creates it. If the key already exists, this same operation updates its value. This dual functionality makes it a common choice for dynamically managing dictionary content.

Common dictionary update techniques

Beyond adding single entries, you can also use more advanced techniques to add multiple items or even merge two dictionaries into one.

Using the update() method to add multiple items

car = {"make": "Toyota", "model": "Corolla"}
car.update({"year": 2022, "color": "blue"})
print(car)--OUTPUT--{'make': 'Toyota', 'model': 'Corolla', 'year': 2022, 'color': 'blue'}

The update() method is your go-to for merging another dictionary or an iterable of key-value pairs into an existing one. It efficiently handles multiple entries in a single operation. In the example, the dictionary with the year and color is merged into the car dictionary.

  • New key-value pairs are simply added.
  • If a key already exists, its value is updated with the new one.

Using dictionary unpacking with ** operator

fruits = {"apple": 5, "banana": 3}
more_fruits = {"orange": 2, "grape": 4}
all_fruits = {**fruits, **more_fruits}
print(all_fruits)--OUTPUT--{'apple': 5, 'banana': 3, 'orange': 2, 'grape': 4}

Dictionary unpacking with the double-asterisk ** operator offers a concise way to merge dictionaries. This operator effectively "unpacks" the key-value pairs from fruits and more_fruits, combining them into a new dictionary called all_fruits.

  • This method is great for creating a new merged dictionary without altering the original ones.
  • If both dictionaries had a common key, the value from the second dictionary (more_fruits) would overwrite the first.

Merging dictionaries with dictionary comprehensions

dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
merged = {k: dict2.get(k, dict1.get(k)) for k in set(dict1) | set(dict2)}
print(merged)--OUTPUT--{'a': 1, 'b': 3, 'c': 4}

Dictionary comprehensions provide a compact and flexible way to merge dictionaries. This technique is especially useful when you need custom logic for handling overlapping keys.

  • The expression set(dict1) | set(dict2) creates a union of all unique keys from both dictionaries.
  • For each key k, the code uses dict2.get(k, dict1.get(k)) to assign a value. This clever trick attempts to get the value from dict2 first, and if the key isn't found, it falls back to getting it from dict1, effectively prioritizing the second dictionary.

Advanced dictionary techniques

Moving beyond basic merging, you can use more advanced tools to gracefully handle missing keys or create a single, dynamic view of multiple dictionaries.

Using collections.defaultdict for nested dictionaries

from collections import defaultdict
user_scores = defaultdict(dict)
user_scores["Alice"]["math"] = 95
user_scores["Alice"]["science"] = 92
print(dict(user_scores))--OUTPUT--{'Alice': {'math': 95, 'science': 92}}

A defaultdict is a lifesaver when working with nested data structures. By initializing it with defaultdict(dict), you tell Python to automatically create a new, empty dictionary whenever you try to access a key that doesn't exist. This completely sidesteps the common KeyError you'd get with a regular dictionary.

  • In the example, user_scores["Alice"] doesn't exist initially, so defaultdict creates it as an empty dictionary on the fly.
  • This lets you immediately add nested keys like ["math"] without extra setup code.

Using setdefault() to append with a default value

contacts = {"John": ["555-1234"]}
contacts.setdefault("John", []).append("555-5678")
contacts.setdefault("Mary", []).append("555-9012")
print(contacts)--OUTPUT--{'John': ['555-1234', '555-5678'], 'Mary': ['555-9012']}

The setdefault() method offers a clean way to handle keys that might not exist yet. It’s a "get-or-set" operation in one move—if the key is found, it returns its value; if not, it creates the key with a default value and then returns that new value. This is especially useful for appending to lists inside a dictionary.

  • For the existing key "John", the method returns his list, and append() adds the new number.
  • For the new key "Mary", it creates an empty list [], which is then ready for the append() operation.

This approach lets you avoid writing an explicit if-else block to check for the key first, making your code more compact.

Using ChainMap for dictionary views

from collections import ChainMap
defaults = {"theme": "dark", "language": "en"}
user_settings = {"theme": "light"}
settings = ChainMap(user_settings, defaults)
print(dict(settings))--OUTPUT--{'theme': 'light', 'language': 'en'}

The ChainMap object from the collections module provides a clever way to link multiple dictionaries into a single, searchable view. It doesn’t actually merge them into a new dictionary. Instead, it groups them and searches through them in the order you provide, which is ideal for managing configurations with default values.

  • When you look up a key, ChainMap checks the first dictionary in the chain—in this case, user_settings—and moves to the next one only if the key isn't found.
  • This is why "theme" is "light" (from user_settings), but "language" is "en" (from defaults).

Move faster with Replit

Replit is an AI-powered development platform that transforms natural language into working applications. You can describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.

The dictionary techniques from this article, like using setdefault() or the ** operator, are the building blocks for real-world software. Replit Agent can turn these concepts into production-ready applications:

  • Build a user profile manager that dynamically adds new attributes like 'email' or 'last_login' to user records.
  • Create a settings manager for an application that overlays user preferences on top of system defaults, similar to how ChainMap works.
  • Deploy a real-time analytics dashboard that merges incoming data streams into a central statistics dictionary using the update() method.

Describe your app idea, and Replit Agent writes the code, tests it, and fixes issues automatically, all in your browser. Try Replit Agent to bring your next project to life.

Common errors and challenges

Even with the right techniques, you might run into issues like key errors, iteration problems, or unexpected changes in nested data.

Avoiding KeyError when accessing non-existent keys

A common pitfall is the KeyError, which Python raises when you try to access a dictionary key that doesn't exist. While methods like setdefault() can prevent this, sometimes you just want to check for a key without modifying the dictionary. In those cases, you have a couple of great options.

  • You can use the in keyword to safely check if a key is present before you try to use it.
  • Alternatively, the get() method lets you retrieve a key's value. If the key isn't found, it returns None instead of causing an error, which keeps your program running smoothly.

Preventing dictionary modification errors during iteration

You can't change the size of a dictionary while you're looping over it. If you try to add or remove keys inside a for loop, Python will stop you with a RuntimeError. This happens because modifying the dictionary messes up the loop's internal counter.

The fix is to iterate over a copy of the dictionary's keys or items. By creating a temporary list of keys with list(my_dict.keys()), you can loop over the static list while safely modifying the original dictionary.

Understanding nested dictionary copying issues

When you copy a dictionary containing other dictionaries or lists, you might not get the result you expect. Using the copy() method or the dict() constructor creates a shallow copy. This means it only copies the top-level key-value pairs—any nested objects are shared between the original and the copy.

If you change a value in a nested dictionary within the copy, the change will also appear in the original. To create a truly independent duplicate, you need a deep copy. The deepcopy() function from Python's copy module recursively copies every element, ensuring your original dictionary remains untouched.

Avoiding KeyError when accessing non-existent keys

Directly accessing a dictionary key is fast, but it's risky. If the key doesn't exist, Python raises a KeyError, which will crash your program if it's not handled. The following code demonstrates this common but disruptive error in action.

user_data = {"name": "Alice", "email": "[email protected]"}
phone = user_data["phone"] # This raises KeyError: 'phone'
print(f"Phone number: {phone}")

The code attempts to access user_data["phone"], but since the "phone" key doesn't exist in the dictionary, Python raises an error. The following example shows how to handle this situation gracefully to avoid a crash.

user_data = {"name": "Alice", "email": "[email protected]"}
phone = user_data.get("phone", "Not available")
print(f"Phone number: {phone}")

The get() method is your solution for safely accessing keys that might not exist. It prevents a KeyError by returning a default value if the key is missing.

  • The code uses user_data.get("phone", "Not available") to look for the "phone" key.
  • Since it’s not found, the method returns the default string "Not available" instead of crashing.

This is especially useful when handling data from external sources like APIs or user forms, where certain fields might be optional.

Preventing dictionary modification errors during iteration

It’s a common trap: you can’t change a dictionary’s size while iterating over it. Trying to add or remove keys inside a for loop will disrupt the process and trigger a RuntimeError. The code below shows exactly what happens when you try.

scores = {"math": 90, "science": 95, "history": 85}
for subject in scores:
if scores[subject] > 90:
scores["honors_" + subject] = True # Modifies dict during iteration
print(scores)

The loop attempts to add a new key to the scores dictionary while still iterating over it. Python prohibits changing a dictionary's size mid-loop, which causes the RuntimeError. The following example demonstrates the correct approach.

scores = {"math": 90, "science": 95, "history": 85}
honors_subjects = {}
for subject in scores:
if scores[subject] > 90:
honors_subjects["honors_" + subject] = True
scores.update(honors_subjects) # Updates after iteration completes
print(scores)

The solution is to collect your changes in a temporary dictionary instead of modifying the original one mid-loop.

  • Inside the loop, add new key-value pairs to a separate dictionary, like honors_subjects.
  • Once the loop is complete, merge the temporary dictionary into the original using the update() method.

This approach neatly separates the iteration from the modification, which prevents the RuntimeError and keeps your code predictable.

Understanding nested dictionary copying issues

It's easy to get tripped up when copying dictionaries that contain other objects, like lists. Using the standard copy() method creates a shallow copy, so changes to nested items in the copy will also affect the original. The following code demonstrates this surprising behavior.

import copy
original = {"user": {"name": "John", "scores": [85, 90]}}
copied = original.copy() # Creates shallow copy
copied["user"]["scores"][0] = 100 # Modifies original too!
print(original["user"]["scores"]) # Shows [100, 90]

The issue is that original.copy() doesn't create a new nested dictionary. Instead, both original and copied point to the same user data. The code below demonstrates how to create a completely separate duplicate.

import copy
original = {"user": {"name": "John", "scores": [85, 90]}}
copied = copy.deepcopy(original) # Creates deep copy
copied["user"]["scores"][0] = 100 # Only affects the copy
print(original["user"]["scores"]) # Still shows [85, 90]

The solution is to use copy.deepcopy(), which creates a fully independent duplicate of the original dictionary, including all nested objects.

  • It recursively copies every element, so the new dictionary doesn't share any references with the original.
  • As a result, modifying a nested list in the copied dictionary won't affect the original at all.

This method is essential whenever you need to ensure your original data remains completely unchanged after making a copy.

Real-world applications

With these techniques and error-handling strategies, you can build powerful tools for tasks like text analysis and function optimization.

Text analysis with a dict frequency counter

One of the most common uses for a dictionary is to build a frequency counter, which you can do by looping through text and using the get() method to increment the count for each word.

text = "to be or not to be that is the question"
word_freq = {}
for word in text.split():
word_freq[word] = word_freq.get(word, 0) + 1
print(word_freq)

This code tallies how many times each word appears in the given string. It loops through every word created by text.split() and uses a clever trick to update a dictionary named word_freq.

  • For each word, the get() method looks up its current count. If the word isn't in the dictionary yet, get() provides a default value of 0.
  • The code then adds one to the count and updates the dictionary, either creating the entry for a new word or incrementing the count for an existing one.

Creating a memoization cache with dict for recursive functions

A dictionary is an excellent tool for memoization, a technique that caches the results of expensive function calls in recursive functions like fibonacci to avoid re-computing them.

cache = {}
def fibonacci(n):
if n in cache:
return cache[n]
if n <= 1:
result = n
else:
result = fibonacci(n-1) + fibonacci(n-2)
cache[n] = result
return result

print(fibonacci(6))
print("Cache:", cache)

This fibonacci function uses a cache dictionary to remember previously calculated values. Before running the full recursive calculation, it first checks if the result for n is already in the cache.

  • If the value exists, it’s returned immediately, which saves a lot of computation.
  • If not, the function calculates the result, stores it in the cache with cache[n] = result, and then returns it.

This process ensures that each Fibonacci number is computed only once, making the function much more efficient, especially for larger numbers.

Get started with Replit

Turn these dictionary skills into a real tool. Tell Replit Agent to “build a word frequency counter from a URL” or “create a user settings manager that merges two configuration dictionaries.”

Replit Agent will write the code, test for errors, and deploy your application. Start building with Replit and bring your idea to life.

Get started free

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.

Get started for free

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.