Append to a Dictionary in Python (There Is No append Method)

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.

Diagram comparing dictionary append attempts with assignment update merge and setdefault in Python
Five ways people try to add to a dictionary, and what each one does.

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.
Command Prompt showing the Python AttributeError that a dict object has no attribute append
.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.
Command Prompt showing how to add a key and value to a Python dictionary by assignment
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'}
Command Prompt showing Python dictionaries merged with the double star syntax and the pipe operator
| and {**a, **b} build a new dictionary; the right-hand side wins.
FormResultOriginals
a.update(b)a is changeda is modified
{**a, **b}A new dictionaryBoth untouched
a | bA new dictionaryBoth untouched
a |= ba is changedSame 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']}
Command Prompt showing how to append to a list stored as a Python dictionary value using setdefault
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