Dictionary Methods
Beginner Data Structures

Python Dictionary Methods

1Dictionary Methods: overview

Dictionary methods are the built-in tools for everyday dictionary jobs: reading with a fallback, creating entries on first use, removing and merging pairs, viewing keys and values, and building new dictionaries.

GoalBy the end you can read, add, remove, merge and copy dictionary entries without a KeyError, use the live key, value and item views, build dictionaries with zip, fromkeys and comprehensions, and sort a dictionary by value.

Before this lesson: Python Dictionaries

2Dictionary Methods: diagram

What happens when the key is missing?What happens when the key is missing?callchanges the dict?key missingreturnsd[k]noKeyErrorthe valued.get(k, x)noreturns xthe value, or xd.setdefault(k, x)only if missingstores k: xthe valued.pop(k, x)removes kreturns xthe removed valuedel d[k]removes kKeyErrornothingd.pop(k) without a default raises KeyError for a missing key too.
Five ways to touch one key. Square brackets and del raise KeyError when the key is missing; get, setdefault and pop with a default fall back to a value you choose.

3Dictionary Methods: explained step by step

The dictionary lesson covered the basics: creating, reading with [], a first look at get(), and looping with items(). This lesson is about the methods you reach for when real data gets untidy: keys that may be missing, entries you must create on first use, pairs to remove, dictionaries to merge, and new dictionaries to build from other data.

get(): read without crashing

d.get(key, default) returns the value if the key exists and the default if it does not. It never adds the key to the dictionary. The default is used only when the key is absent: a key whose value is None or 0 is present, so you get that value back. To test for a key, use in, which looks at the keys, not the values.

Python
stock = {"apples": 12, "pears": 0, "plums": None}
print(stock.get("pears", 5), stock.get("figs", 5), stock.get("plums", 5))
print("plums" in stock, 12 in stock.values(), 12 in stock)

setdefault(): read, and create the entry if it is missing

d.setdefault(key, default) returns the value for key. If the key is missing, it first stores the default and then returns it. That makes it ideal for grouping, where every key needs a fresh list the first time you meet it.

Python
groups = {}
for student, team in [("Asha", "red"), ("Ben", "blue"), ("Cara", "red")]:
    groups.setdefault(team, []).append(student)
print(groups)

The difference from get() matters: setdefault keeps the default, get forgets it. That is why groups.get(team, []).append(student) looks right but does nothing useful. It appends to a throwaway list.

Removing: pop, popitem, del and clear

Four tools remove entries, and they differ in what they give back and in how they treat a missing key.

Python
cart = {"pen": 2, "ink": 1, "pad": 3, "cup": 4}
print(cart.pop("ink"))
print(cart.pop("glue", 0))
print(cart.popitem())
del cart["pen"]
print(cart)
cart.clear()
print(cart)

pop(key) removes the key and returns its value; give it a second argument and a missing key returns that instead of raising KeyError. popitem() removes the most recently added pair and returns it as a tuple (and raises KeyError on an empty dictionary). del d[key] removes without returning anything and raises KeyError if the key is missing. clear() empties the dictionary.

Merging: |, |= and update()

a | b builds a new dictionary from both; when a key appears on both sides, the value from the right-hand side wins. a |= b and a.update(b) change a itself. update() also accepts keyword arguments when the keys are simple names.

Python
defaults = {"theme": "light", "font": 14, "autosave": True}
prefs = {"theme": "dark", "font": 16}
merged = defaults | prefs
print(merged)
defaults.update(prefs)
print(defaults == merged)
defaults |= {"lang": "en"}
defaults.update(font=18, autosave=False)
print(defaults)

There is no + for dictionaries. Like list methods, update() returns None, so never write settings = settings.update(...).

copy(): an independent top level

b = a gives the dictionary a second name, not a second dictionary. a.copy() creates a new one, so changing one no longer affects the other. It is a shallow copy: nested lists or dictionaries inside are still shared (see the lesson on copying).

Python
base = {"a": 1}
alias = base
clone = base.copy()
alias["a"] = 99
print(base, clone)

Live views: keys(), values() and items()

These methods return views, windows onto the dictionary rather than copies. If the dictionary changes later, an existing view shows the change. Key views also support set operations such as & and -.

Python
prices = {"tea": 3, "cake": 4}
names = prices.keys()
prices["milk"] = 2
print(list(names))
print(sorted(prices.keys() & {"tea", "milk", "juice"}))

for name in list(prices):
    if prices[name] < 3:
        del prices[name]
print(prices)

You may not add or remove keys while looping over a view; Python raises RuntimeError. Looping over list(prices) (a snapshot) makes the deletion safe.

Building dictionaries

dict.fromkeys(keys, value) gives every key the same starting value. dict(zip(keys, values)) pairs two lists, and dict() also accepts a list of (key, value) pairs. A dict comprehension filters or inverts an existing dictionary:

Python
slots = dict.fromkeys(["mon", "tue", "wed"], 0)
names = ["Asha", "Ben", "Cara"]
marks = [82, 91, 77]
scores = dict(zip(names, marks))
print(slots)
print(scores)

passed = {n: s for n, s in scores.items() if s >= 80}
by_score = {s: n for n, s in scores.items()}
print(passed)
print(by_score)

Inverting only works cleanly when the values are unique and hashable: two names with the same score would collapse into one key.

Order and sorting

A dictionary remembers insertion order. Changing a value keeps the key's position; deleting a key and adding it again moves it to the end. To order a dictionary by value, sort its items and build a new dictionary.

Python
ranks = {"Ben": 91, "Asha": 82, "Cara": 77}
ranks["Asha"] = 85
del ranks["Ben"]
ranks["Ben"] = 91
print(list(ranks))
print(dict(sorted(ranks.items(), key=lambda pair: pair[1], reverse=True)))

Which tool for a missing key?

  • The key must exist: d[key]. A KeyError is the right alarm.
  • It might be missing and you just need a value: d.get(key, default).
  • It might be missing and you want to create it: d.setdefault(key, default).
  • Remove it if it is there: d.pop(key, None).
  • Only act when it exists: if key in d:.

Quick reference: Dictionary Methods

4Dictionary Methods: syntax

d.get(key, default)           # read; never adds the key
d.setdefault(key, default)    # read; stores default if key is missing
d.pop(key, default)           # remove and return; default avoids KeyError
d.popitem()                   # remove the last-added (key, value) pair
del d[key]                    # remove; KeyError if missing
d.clear()                     # remove everything
new = a | b                   # merge into a new dict (right side wins)
a |= b      a.update(b)       # merge into a, in place
d.copy()                      # shallow copy
d.keys()  d.values()  d.items()   # live views
key in d       value in d.values()
dict.fromkeys(keys, value)    # same starting value for every key
dict(zip(keys, values))       # build from two lists
{k: v for k, v in d.items() if v > 0}   # filter or invert

5Dictionary Methods: run the example

Press Run (or Ctrl + Enter). The output (step 6) appears directly below the code. Change anything and run it again.

Interactive example
orders = [("Asha", "tea"), ("Ben", "cake"), ("Cara", "tea"), ("Dev", "juice")]
menu = {"tea": 3, "cake": 4}

by_item = {}
for customer, item in orders:
    by_item.setdefault(item, []).append(customer)
print(by_item)

menu.update({"juice": 2, "cake": 5})
total = 0
for item, buyers in by_item.items():
    total += menu.get(item, 0) * len(buyers)
print("Total:", total)

removed = menu.pop("cake", None)
print("Removed price:", removed, "| menu:", menu)
Expected output{'tea': ['Asha', 'Cara'], 'cake': ['Ben'], 'juice': ['Dev']}
Total: 13
Removed price: 5 | menu: {'tea': 3, 'juice': 2}

7Dictionary Methods: how the example works

Hover or tap a step to highlight its lines in the example above.

  1. Line 1-2orders is a list of (customer, item) pairs. menu maps each item to its price, but it does not know about juice yet.
  2. Line 4by_item starts as an empty dictionary that will map each item to the list of people who ordered it.
  3. Line 5-6For every order, setdefault(item, []) returns the list stored under item, creating an empty one the first time the item appears. .append(customer) then adds the customer to that stored list.
  4. Line 7The groups come out in the order the items first appeared: tea (Asha, Cara), cake (Ben), juice (Dev).
  5. Line 9update() merges the pairs into menu: juice is added, and cake is overwritten with its new price of 5.
  6. Line 10-13The loop reads each item and its buyers from by_item.items(). menu.get(item, 0) is the price, or 0 if an item were missing from the menu, and it is multiplied by the number of buyers: 32 + 51 + 2*1 = 13, which line 13 prints.
  7. Line 15pop("cake", None) removes the cake entry and returns its price, 5. The default None means a missing key would not raise an error.
  8. Line 16The menu has lost cake but kept tea and juice, in their original order.

8Dictionary Methods: common mistakes

Using get() when you meant setdefault()

✗ Wrong

groups = {}
for team, name in [("red", "Asha"), ("blue", "Ben"), ("red", "Cara")]:
    groups.get(team, []).append(name)
print(groups)
No error, but it prints {}: nothing was stored

✓ Right

groups = {}
for team, name in [("red", "Asha"), ("blue", "Ben"), ("red", "Cara")]:
    groups.setdefault(team, []).append(name)
print(groups)

get() returns the default without storing it, so each name is appended to a throwaway list. setdefault() stores the new list in the dictionary and returns it.

dict.fromkeys() with a list as the value

✗ Wrong

week = dict.fromkeys(["mon", "tue"], [])
week["mon"].append("gym")
print(week)
No error, but it prints {'mon': ['gym'], 'tue': ['gym']}: both keys share one list

✓ Right

week = {day: [] for day in ["mon", "tue"]}
week["mon"].append("gym")
print(week)

fromkeys() stores the very same object under every key. That is fine for numbers and strings, but a list or dictionary must be created separately for each key, which a dict comprehension does.

Deleting keys while looping over the dictionary

✗ Wrong

stock = {"pen": 0, "ink": 4, "pad": 0}
for name, qty in stock.items():
    if qty == 0:
        del stock[name]
print(stock)
RuntimeError: dictionary changed size during iteration

✓ Right

stock = {"pen": 0, "ink": 4, "pad": 0}
for name, qty in list(stock.items()):
    if qty == 0:
        del stock[name]
print(stock)

You cannot add or remove keys while a loop is walking through the dictionary. Loop over a snapshot taken with list(...), or build a new dictionary with a comprehension.

Popping a key that is not there

✗ Wrong

cart = {"pen": 2}
removed = cart.pop("glue")
print(removed)
KeyError: 'glue'

✓ Right

cart = {"pen": 2}
removed = cart.pop("glue", None)
print(removed)

pop(key) with no default raises KeyError for a missing key, just like d[key]. Pass a second argument to get that value back instead.

Testing for a value with in

✗ Wrong

stock = {"apples": 12, "pears": 0}
if 12 in stock:
    print("Found 12")
else:
    print("No 12 in stock")
No error, but it prints No 12 in stock: `in` looks at the keys, not the values

✓ Right

stock = {"apples": 12, "pears": 0}
if 12 in stock.values():
    print("Found 12")
else:
    print("No 12 in stock")

On a dictionary, x in d asks whether x is a key. To look through the values, use x in d.values().

9Dictionary Methods: exercises

Write your answer, then press Check. The checks run in your browser.

1. Settings with defaults

Write code Easy

Complete with_defaults(settings, defaults). It must return a new dictionary that contains every default, with the user's settings overriding any default that has the same key. Neither argument may be changed.

Your code
def with_defaults(settings, defaults):
    # return a NEW dict: all defaults, overridden by settings
    pass

print(with_defaults({"theme": "dark"}, {"theme": "light", "font": 14}))

2. Predict the dictionary methods

Predict the output Easy

Read the code and type exactly what it prints, line by line. Then press Check to run it and compare.

Read this code
tally = {"a": 1}
print(tally.setdefault("a", 10))
print(tally.setdefault("b", 20))
print(tally.get("c", 30))
print(tally)
print(tally.pop("b"))
print(tally.popitem())
print(tally)

2 more exercises → 6 more examples

Finished this lesson?
Progress is saved in this browser — create a free account to keep it across devices.

Questions learners ask

How do I merge two dictionaries in Python?

Use a | b to create a new dictionary (Python 3.9 and later), or a |= b and a.update(b) to change a in place. {**a, **b} also works in older versions. When both dictionaries have the same key, the value from the right-hand side wins. Dictionaries do not support +.

What is the difference between pop(), popitem() and del on a dictionary?

pop(key) removes the key and returns its value, and pop(key, default) returns the default instead of raising KeyError. popitem() removes and returns the most recently added (key, value) pair. del d[key] removes the key and returns nothing, and raises KeyError if the key is missing.

When should I use setdefault() instead of get()?

Use get() when you only want to read a value and fall back to a default; it never changes the dictionary. Use setdefault() when the default should also be stored, such as creating an empty list the first time a key appears so you can append to it straight away.

Further reading

10Next lesson

Up nextPython Nested Data StructuresA nested data structure is a list, dict or tuple that contains other lists or dicts, and you reach a value deep inside …

Related:
Python DictionariesPython Nested Data StructuresPython collections ModulePython KeyError: What It Means and How to Fix It

← Python Dictionaries · All Data Structures lessons · Practise in the IDE