Python KeyError: missing keys, get() and in
Fix a Python KeyError by inspecting the key you actually look up. Choose required keys, defaults or counters without confusing missing keys, zero and None.
Your program stops with KeyError: 'coffee'. The message names the key it tried to find. Replacing every lookup with get() can hide the mistake instead of fixing it.
In short
A KeyError from a lookup in an ordinary Python dictionary means the requested key is missing. Check the key's exact value and type, then follow your data contract: use d[key] for a required key, d.get(key, default) for an expected absence, or key in d to test membership. A stored value of 0 or None doesn't mean the key is missing.
What does the key in KeyError mean?
In stock["coffee"], Python looks for the key "coffee", rather than the first available value. This dictionary only contains "tea".
stock = {"tea": 0}print(stock["coffee"])$ python3 missing.pyTraceback (most recent call last): File "/home/student/lab/missing.py", line 2, in <module> print(stock["coffee"]) ~~~~~^^^^^^^^^^KeyError: 'coffee'Read the last line of the traceback for the key, then find the failing lookup in your file. If a line contains several lookups, temporarily split it into separate statements. Identify which dictionary is being read at that point.
These examples use ordinary dictionaries. Other objects can also raise KeyError, and a dictionary subclass can provide its own missing-key behavior.
Why does the key look as though it exists?
The string "tea " has a trailing space. It's a different key from "tea". Use repr() to expose the string's boundaries, then inspect its type and the keys in the dictionary you're actually reading.
stock = {"tea": 0, "1": 8}key = "tea "print(repr(key), type(key).__name__)print(list(stock))print(key in stock)print("tea" in stock)print(1 in stock, "1" in stock)$ python3 inspect.py'tea ' str['tea', '1']FalseTrueFalse TrueCheck capitalization too, and make sure you're inspecting the right object. Two dictionaries may contain different keys. The string "1" and the integer 1 are different keys. A missing list position usually raises IndexError; a dictionary can have an integer key without treating it as a position.
Don't automatically strip spaces or lowercase every key. Normalize only when your input format calls for it. Those transformations would change the data in a case-sensitive character counter.
Should you use get(), in or square brackets?
Decide what a missing key means to your program. A missing required field is useful evidence of invalid input. To explain that failure to the caller, catch KeyError around the relevant lookup. A bare except: around the whole function would also hide unrelated failures.
What does a missing key mean?
Required field
d[key]
Detect the absence and fix the data or the caller.
Optional value
d.get(key, default)
The program has a defined fallback for this case.
Membership check
key in d
Test the key even when its stored value is zero or None.
get() reads without adding a key. It uses the fallback only when the key is absent.
counts = {"a": 2, "b": 1}print(counts.get("c", 0))print("c" in counts)print(counts)print(counts.get("a", 0))$ python3 lookup.py0False{'a': 2, 'b': 1}2Storing a value requires an assignment, as in the counter below.
Why can if not d.get(key) give the wrong answer?
That condition tests the returned value. It succeeds for 0, None, False and an empty string, even when the key exists.
stock = {"tea": 0, "coffee": None}for key in ("tea", "coffee", "milk"): print(key, key in stock, stock.get(key), not stock.get(key))$ python3 presence.pytea True 0 Truecoffee True None Truemilk False None True"tea" exists with a value of zero. "coffee" exists with an unknown value represented by None. "milk" is missing. Use membership when those situations need different handling.
A private sentinel also lets you retrieve a value in one lookup while distinguishing absence from a stored None:
missing = object()stock = {"coffee": None}for key in ("coffee", "milk"): value = stock.get(key, missing) print(key, value is missing, value is None)$ python3 sentinel.pycoffee False Truemilk True FalseCompare the sentinel using is. The object created for this check is distinct from every value already stored in this dictionary.
How do you initialize a counter without KeyError?
In an empty dictionary, counts[char] += 1 must first read a value that doesn't exist yet. A counter has a natural initial value of zero. Read that fallback, add one, then assign the result.
def frequencies(text): counts = {} for char in text: counts[char] = counts.get(char, 0) + 1 return counts
for prefix in ("", "a", "ab", "aba"): print(repr(prefix), frequencies(prefix))$ python3 counter.py'' {}'a' {'a': 1}'ab' {'a': 1, 'b': 1}'aba' {'a': 2, 'b': 1}For "aba", the lookup of "a" returns zero on its first visit and one on its last. The assignment creates the key the first time, then replaces its value on the next visit.
Step 1 of 3
After the first a
- char
- a
- a1created
- bmissing
get returns zero, then the assignment stores one.
Read all steps
1. After the first a
- char
- a
- a1created
- bmissing
get returns zero, then the assignment stores one.
2. After b
- char
- b
- a1
- b1created
A second key is added; the count for a stays unchanged.
3. After the last a
- char
- a
- a2incremented
- b1
get reads one; the assignment stores two under the same key.
frequencies creates an empty dictionary on every call. Empty input returns an empty dictionary. It preserves every character, including spaces and capitalization.
same_character_bag
Compare the character frequencies of two strings. The exercise requires matching characters with matching counts; spaces and capitalization count, and two empty strings match. A set alone would lose the frequencies.
To order results afterward, see sorting Python lists by multiple keys. If a change appears in several structures, copying nested lists explains that separate mechanism.
Frequently asked questions
Does get() insert a missing key?
No. It returns the fallback and leaves the dictionary unchanged. The counter's assignment adds or updates the key.
Why does get(key, 0) return None?
If the key exists with a stored value of None, Python returns that value. The fallback 0 applies only to a missing key.
Should I catch every KeyError?
Catch a missing key when your program knows how to handle it. For a required field, fix the input or report the missing field. A silent fallback can produce an incorrect result.
Does in search the values?
For a dictionary, key in d checks keys. Searching its values is a different operation that answers a different question.
Sources
