Python dictionaries have no .append() method. You add to one by assigning to a key:
scores = {"ann": 90}
scores["bob"] = 85 # {"ann": 90, "bob": 85}
Calling .append() on a dictionary raises AttributeError, because that method belongs to lists. A list adds to the end; a dictionary needs you to say which key.
What follows covers adding one key, adding several, merging two dictionaries, and the case most people actually mean: appending to a list stored as a value. Output is from Python 3.12.5.

Why dict.append() does not exist
The error is clear about it:
scores = {"ann": 90}
print("does a dictionary have .append()?", hasattr(dict, "append"))
print()
try:
scores.append("bob")
except AttributeError as err:
print("scores.append('bob') -> AttributeError:", err)
print()
print("append belongs to lists. A list has positions to add to the end of.")
print("A dictionary has keys, so you say which key you mean.")
Output:
does a dictionary have .append()? False
scores.append('bob') -> AttributeError: 'dict' object has no attribute 'append'
append belongs to lists. A list has positions to add to the end of.
A dictionary has keys, so you say which key you mean.

.append() is a list method, so a dictionary rejects it.A list is a sequence, so “append” has an obvious meaning: put this at the end. A dictionary has no end to append to, only keys, so adding always involves naming one.
Add a key and value to a dictionary
Assignment does the job, and also does the updating:
scores = {"ann": 90}
scores["bob"] = 85
scores["cat"] = 95
print("after two assignments ->", scores)
print()
print("that is the whole operation. There is no method to call.")
print()
print("but an existing key OVERWRITES rather than adding:")
scores["ann"] = 100
print(" scores['ann'] = 100 ->", scores)
print(" still", len(scores), "keys")
print()
print("keys are unique, so a dictionary can never hold 'ann' twice.")
Output:
after two assignments -> {'ann': 90, 'bob': 85, 'cat': 95}
that is the whole operation. There is no method to call.
but an existing key OVERWRITES rather than adding:
scores['ann'] = 100 -> {'ann': 100, 'bob': 85, 'cat': 95}
still 3 keys
keys are unique, so a dictionary can never hold 'ann' twice.

scores["bob"] = 85 adds the key; assigning to an existing key replaces it.That second behaviour catches people out. Assigning to a key that already exists overwrites the value rather than adding a second entry, because keys are unique.
If you need to keep both values, the value has to be a list, which is covered below. To check first, see whether a key exists.
Add several items with update()
update() takes a dictionary, keyword arguments or pairs:
scores = {"ann": 90}
scores.update({"bob": 85, "cat": 95})
print("update with a dictionary ->", scores)
scores.update(dan=70, eve=88)
print("update with keywords ->", scores)
scores.update([("fay", 60)])
print("update with pairs ->", scores)
print()
print("update() changes the dictionary in place and returns None,")
print("so never write scores = scores.update(...)")
result = {"a": 1}.update({"b": 2})
print(" the return value is ->", result)
Output:
update with a dictionary -> {'ann': 90, 'bob': 85, 'cat': 95}
update with keywords -> {'ann': 90, 'bob': 85, 'cat': 95, 'dan': 70, 'eve': 88}
update with pairs -> {'ann': 90, 'bob': 85, 'cat': 95, 'dan': 70, 'eve': 88, 'fay': 60}
update() changes the dictionary in place and returns None,
so never write scores = scores.update(...)
the return value is -> None
It changes the dictionary in place and returns None. Writing scores = scores.update(...) therefore throws your dictionary away and leaves you with None, which is a memorable afternoon.
Existing keys are overwritten by the incoming values, exactly as single assignment does.
Add one dictionary to another
Three ways to combine, and they differ in what they change:
defaults = {"theme": "light", "size": 12}
user = {"size": 14, "font": "Inter"}
print("defaults ->", defaults)
print("user ->", user)
print()
print("{**defaults, **user} ->", {**defaults, **user})
print("defaults | user ->", defaults | user)
print(" the right-hand side wins on 'size'")
print()
print("both leave the originals alone:")
print(" defaults is still ->", defaults)
print()
merged = dict(defaults)
merged |= user
print("|= merges into the left one:")
print(" merged ->", merged)
Output:
defaults -> {'theme': 'light', 'size': 12}
user -> {'size': 14, 'font': 'Inter'}
{**defaults, **user} -> {'theme': 'light', 'size': 14, 'font': 'Inter'}
defaults | user -> {'theme': 'light', 'size': 14, 'font': 'Inter'}
the right-hand side wins on 'size'
both leave the originals alone:
defaults is still -> {'theme': 'light', 'size': 12}
|= merges into the left one:
merged -> {'theme': 'light', 'size': 14, 'font': 'Inter'}

| and {**a, **b} build a new dictionary; the right-hand side wins.| Form | Result | Originals |
|---|---|---|
a.update(b) | a is changed | a is modified |
{**a, **b} | A new dictionary | Both untouched |
a | b | A new dictionary | Both untouched |
a |= b | a is changed | Same as update |
The | operator arrived in Python 3.9 and reads best. Use the {**a, **b} form if you need to support older versions.
Where both dictionaries hold the same key, the one on the right wins. That is what makes this pattern good for applying user settings over defaults.
Append to a list inside a dictionary
This is the question behind most searches for “dictionary append”:
words = ["ant", "bee", "auk", "bat"]
print("what people usually mean by 'append to a dictionary':")
print("adding to a LIST that is stored as a value.")
print()
print("a plain dictionary cannot do it directly:")
plain = {}
try:
plain["a"].append("ant")
except KeyError as err:
print(" plain['a'].append('ant') -> KeyError:", err)
print(" the key does not exist yet, so there is no list to append to")
print()
groups = {}
for word in words:
groups.setdefault(word[0], []).append(word)
print("setdefault creates the list on first use:")
print(" ", groups)
print()
from collections import defaultdict
auto = defaultdict(list)
for word in words:
auto[word[0]].append(word)
print("defaultdict(list) does the same with no setdefault:")
print(" ", dict(auto))
Output:
what people usually mean by 'append to a dictionary':
adding to a LIST that is stored as a value.
a plain dictionary cannot do it directly:
plain['a'].append('ant') -> KeyError: 'a'
the key does not exist yet, so there is no list to append to
setdefault creates the list on first use:
{'a': ['ant', 'auk'], 'b': ['bee', 'bat']}
defaultdict(list) does the same with no setdefault:
{'a': ['ant', 'auk'], 'b': ['bee', 'bat']}

setdefault(key, []).append(value) creates the list on first use.A plain dictionary raises KeyError the first time, because there is no list there yet to append to. Something has to create it.
setdefault(key, []) returns the existing list, or inserts an empty one and returns that, so the .append() on the end always has something to work with.
defaultdict(list) removes even that step by building a list automatically for any missing key. Both are worth knowing; defaultdict is cleaner in a loop, setdefault avoids an import. See iterating a dictionary for reading the result back.
Add only if the key is missing
setdefault() on its own is the “do not overwrite” version of assignment:
settings = {"theme": "dark"}
print("setdefault writes only when the key is missing:")
print(" setdefault('theme', 'light') ->", settings.setdefault("theme", "light"))
print(" setdefault('size', 12) ->", settings.setdefault("size", 12))
print(" settings ->", settings)
print()
print("theme kept its existing value; size was added.")
print()
print("the same idea written out:")
settings = {"theme": "dark"}
if "size" not in settings:
settings["size"] = 12
print(" ", settings)
print()
print("setdefault returns the value either way, which is what makes")
print("the .append() chain in the previous example work.")
Output:
setdefault writes only when the key is missing:
setdefault('theme', 'light') -> dark
setdefault('size', 12) -> 12
settings -> {'theme': 'dark', 'size': 12}
theme kept its existing value; size was added.
the same idea written out:
{'theme': 'dark', 'size': 12}
setdefault returns the value either way, which is what makes
the .append() chain in the previous example work.
It returns the value that ends up in the dictionary, whether that was already there or has just been inserted. The long form with if key not in d does the same and is arguably clearer.
Adding to a nested dictionary
Each level is just another dictionary:
students = {"ann": {"maths": 90}}
students["ann"]["art"] = 70
print("adding to an inner dictionary ->", students)
print()
students.setdefault(&qu