How to append to a dictionary in Python
Learn how to append to a Python dictionary. Explore various methods, tips, real-world examples, and common error debugging.

Python dictionaries are a fundamental data structure. You often need to add new key-value pairs to them. This process is essential for dynamic data management in many applications.
In this article, we'll explore techniques to add data, from simple assignment to the update() method. You'll find practical tips, see real-world applications, and get advice to debug common issues.
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 direct method for adding a new item to a dictionary is by assigning a value to a new key using square bracket notation. In the example, the line student["grade"] = "A" creates a new key, "grade", and assigns it the value "A". This approach is efficient and pythonic for adding single entries.
This syntax is powerful because it also handles updates. If the key already exists, the same operation overwrites the existing value. This dual-purpose behavior makes direct assignment a fundamental tool for managing dictionary data dynamically.
Common dictionary update techniques
While adding single items is straightforward, you'll also need efficient ways to merge entire dictionaries or add multiple key-value pairs in one go.
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 dictionaries or adding multiple items at once. It takes another dictionary as an argument—in this case, {"year": 2022, "color": "blue"}—and adds its contents to the original dictionary, modifying it in-place.
- If a key from the new dictionary already exists, its value is updated.
- If the key is new, the key-value pair is simply added.
This makes update() perfect for efficiently combining data from different sources.
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}
Introduced in Python 3.5, the double-asterisk ** operator offers a concise way to merge dictionaries. It unpacks the key-value pairs from existing dictionaries into a new one. This method creates a completely new dictionary, leaving the original ones unchanged—a key difference from the in-place update() method. For comprehensive coverage of merging dictionaries in Python, explore additional techniques and best practices.
- In the expression
{**fruits, **more_fruits}, both dictionaries are unpacked side-by-side. - If keys overlap, the value from the rightmost dictionary (in this case,
more_fruits) would win.
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 offer a highly flexible way to merge dictionaries, giving you precise control over the outcome. While more verbose than other methods, it’s powerful for custom merge logic.
- The expression
set(dict1) | set(dict2)first creates a union of all unique keys from both dictionaries. - The comprehension then iterates over each key, using
dict2.get(k, dict1.get(k))to decide the value. This logic prioritizes values fromdict2for any overlapping keys, falling back todict1otherwise.
Advanced dictionary techniques
Sometimes you need to do more than just add keys, like gracefully handling missing ones or combining dictionaries without modifying the originals.
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}}
The defaultdict is a lifesaver when building nested dictionaries. It behaves like a regular dictionary but provides a default value for keys that don't exist, which helps you avoid a KeyError. Here, defaultdict(dict) instructs Python to automatically create an empty dictionary for any new key you access. This builds on the fundamentals of creating dictionaries in Python.
- This allows you to assign a value like
user_scores["Alice"]["math"] = 95without first initializing the inner dictionary for "Alice". - It’s an elegant way to group related data without writing 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 concise way to add a key only if it doesn't already exist. It's especially useful when you're working with values that are lists or other collections, as it combines checking for a key and adding it into one atomic operation.
- If the key exists (like
"John"),setdefault()returns its current value, allowing you to append to the existing list. - If the key is missing (like
"Mary"), it inserts the key with the specified default value—an empty list[]in this case—and then returns that new list, ready for you to append to it.
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 class from the collections module links multiple dictionaries into a single, updatable view without merging them. It's perfect for managing layered configurations, like default settings that can be overridden by user-specific ones.
- When you look up a key,
ChainMapsearches the dictionaries in the order they were passed—from left to right—and returns the first value it finds. - In this case, it checks
user_settingsbeforedefaults, which is why the user's"light"theme wins over the default.
This approach creates a logical combination while keeping the original dictionaries separate and unmodified.
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. You can immediately apply what you've learned about dictionaries without any environment configuration.
Instead of piecing together techniques, you can use Agent 4 to build complete applications from a simple description. The Agent handles writing the code, connecting to databases, and managing deployment. For example, you could build:
- A user profile manager that groups multiple data points, like emails and phone numbers, under a single user key.
- A settings generator that merges a base configuration with a user's custom choices to produce a final settings dictionary.
- A simple inventory tracker that dynamically adds new items and their quantities as they are scanned.
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
While powerful, dictionaries have a few common traps that can trip you up, from unexpected errors to tricky data modification rules.
Avoiding KeyError when accessing non-existent keys
One of the most frequent issues is the KeyError. This error pops up when you try to access a key that doesn't exist in the dictionary. Instead of letting your program crash, you can handle this gracefully in a few ways:
- Use the
inkeyword to check for the key's presence before you try to use it. - Call the
get()method, which returnsNoneor a default value you specify if the key is missing. This avoids the error entirely. - Wrap your code in a
try...except KeyErrorblock. This approach is great when you expect the key to exist most of the time.
Preventing dictionary modification errors during iteration
Another classic mistake is trying to add or remove items from a dictionary while you're looping over it. Doing so will trigger a RuntimeError because you're changing the dictionary's size mid-iteration, which confuses the loop.
The fix is simple: iterate over a copy of the keys instead of the dictionary itself. You can create a static list of keys with list(my_dict) and loop over that, allowing you to safely modify the original dictionary inside the loop. When debugging these issues, code repair tools can help identify and fix these common patterns automatically.
Understanding nested dictionary copying issues
Finally, be mindful of how you copy dictionaries, especially nested ones. Using the copy() method creates a shallow copy, which means it only copies the top-level dictionary. Any nested dictionaries or lists are just references—not new copies.
If you change a value in a nested dictionary within the copied version, the original dictionary will change too. To create a fully independent duplicate, use copy.deepcopy() from Python's copy module. It recursively copies everything, ensuring your original data remains untouched.
Avoiding KeyError when accessing non-existent keys
Avoiding KeyError when accessing non-existent keys
A KeyError is one of the most common issues you'll face. It happens when you try to access a dictionary key that doesn't exist, which immediately stops your program. This is a frequent source of bugs in data processing tasks.
The following code demonstrates what happens when you request a key—in this case, "phone"—that isn't in the dictionary.
user_data = {"name": "Alice", "email": "alice@example.com"}
phone = user_data["phone"] # This raises KeyError: 'phone'
print(f"Phone number: {phone}")
This direct lookup for user_data["phone"] fails because the key was never defined in the dictionary. Python has no choice but to raise an error. The next example shows how to prevent this crash by checking for the key first.
user_data = {"name": "Alice", "email": "alice@example.com"}
phone = user_data.get("phone", "Not available")
print(f"Phone number: {phone}")
The get() method provides a safe way to access keys. It searches for "phone", but since the key is missing, it returns the default value you specified—"Not available". This approach completely avoids a KeyError, allowing your program to continue smoothly. It's especially useful when dealing with optional data, such as user profiles or API responses where certain fields might not always be present. For more detailed techniques on accessing dictionary keys safely, you can explore additional methods.
Preventing dictionary modification errors during iteration
Preventing dictionary modification errors during iteration
Modifying a dictionary while looping over it is a classic Python pitfall. This action changes the dictionary's size during iteration, which confuses the loop and triggers a RuntimeError. The following code demonstrates what happens when you try to add a key mid-loop.
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 iterates over the scores dictionary directly. When the code finds a score over 90, it tries to add a new key. This alters the dictionary's size mid-loop, which causes the RuntimeError. The correct approach is shown below.
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, like honors_subjects, while you iterate. After the loop finishes, you can safely merge the new items into the original dictionary using the update() method. This two-step process—iterate first, then update—ensures you don’t change the dictionary’s size while looping over it. This pattern is essential when you need to add new entries based on existing values in your data.
Understanding nested dictionary copying issues
Copying nested dictionaries can be tricky. The standard copy() method creates a shallow copy, meaning it only duplicates the top-level dictionary. Any nested dictionaries or lists are just referenced, not copied, leading to unexpected changes in your original data.
The following code demonstrates this problem. Notice how modifying a value in the copied dictionary also alters the original.
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 copy() method doesn't duplicate nested structures. When copied["user"]["scores"] is modified, it changes the list that original also points to. See how to create a truly independent copy in the next example.
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]
To create a truly independent duplicate, use copy.deepcopy(). This function recursively copies every nested object, ensuring your original data remains untouched. Unlike a shallow copy, modifying the new dictionary won't affect the original. It's crucial when you're working with complex data structures, like configuration files or user profiles, and need to preserve the original state while making changes to a temporary version. Learn more comprehensive techniques for copying dictionaries properly in Python.
Real-world applications
Now that you can navigate the methods and pitfalls, you can build practical tools like frequency counters and memoization caches.
Text analysis with a dict frequency counter
By combining a loop with the get() method, you can efficiently build a frequency counter that tallies every word in a piece of text.
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 snippet showcases an elegant pattern for aggregating data. As the code loops through each word, it uses the line word_freq[word] = word_freq.get(word, 0) + 1 to build a dictionary.
- The
get()method looks for the currentword. If the word isn't found, it returns a default value of0. - The code then adds
1to this value—either the existing count or the default0—and updates the dictionary.
This approach is powerful because it handles both new and existing keys in a single, concise expression.
Creating a memoization cache with dict for recursive functions
You can use a dictionary to create a simple yet powerful cache for memoization, which speeds up recursive functions by storing results and avoiding repeated computations.
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)
The fibonacci function uses the cache dictionary to remember previously computed values. This optimization technique is known as memoization.
- Before calculating, it checks if the result for
nalready exists in thecache. If so, it returns the stored value instantly. - If not, it computes the result, adds it to the cache using
cache[n] = result, and then returns it.
This process ensures that each Fibonacci number is calculated only once, drastically speeding up the function by preventing redundant work in the recursive calls. This type of optimization demonstrates how vibe coding can help you rapidly prototype and test algorithmic improvements.
Get started with Replit
Now, turn these dictionary techniques into a real tool. Tell Replit Agent to “build a word frequency counter from a URL” or “create a script that merges default and user config dictionaries.”
The Agent writes the code, tests for errors, and handles deployment for you. Start building with Replit and see your project run in seconds.
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.



