To check whether a key exists in a Python dictionary, use the in operator: if "email" in person:. It is fast, it reads like English, and it never raises. The older has_key() method was removed in Python 3, and wrapping the lookup in a try/except is usually more code for no benefit. This guide covers when in is right and when .get() or setdefault() is better.
The basic check#
person = {"name": "Ana", "age": 34}
if "name" in person:
print("found:", person["name"])
if "email" not in person:
print("no email on file")
The one thing to remember: in looks at keys, not values.
print("Ana" in person) # False - Ana is a value
print("Ana" in person.values()) # True
print("name" in person.keys()) # True, but the .keys() is redundant
Writing in person.keys() is not wrong, just noisy. in person does exactly the same thing and is what other Python programmers expect to read.
Why has_key is gone#
person.has_key("name")
AttributeError: 'dict' object has no attribute 'has_key'
It was removed in Python 3. If you see it in a tutorial, that tutorial is written for Python 2 and everything else in it should be treated with suspicion too. The replacement is in, which is shorter and works on lists, sets, strings and tuples as well.
Often you do not need the check at all#
This pattern is extremely common and slightly wasteful:
if "email" in person:
email = person["email"]
else:
email = "not provided"
.get() collapses it to one line, and looks the key up once instead of twice:
email = person.get("email", "not provided")
email = person.get("email") # None if missing
Use the explicit in check when the two branches do genuinely different things. Use .get() when you just want a fallback value.
Checking several keys at once#
required = {"name", "email", "age"}
if required.issubset(person):
print("all present")
missing = required - person.keys()
if missing:
print("missing:", ", ".join(sorted(missing)))
person.keys() behaves like a set, so you can subtract from it directly. That is a much clearer way to report which fields are missing than a loop with a list of names.
if any(k in person for k in ("phone", "mobile")):
print("some contact number exists")
if all(k in person for k in required):
print("all present")
Removing the check from loops#
Counting or grouping usually starts out like this:
counts = {}
for word in words:
if word in counts:
counts[word] += 1
else:
counts[word] = 1
Three shorter versions, in increasing order of how idiomatic they are:
for word in words:
counts[word] = counts.get(word, 0) + 1
from collections import defaultdict
counts = defaultdict(int)
for word in words:
counts[word] += 1
from collections import Counter
counts = Counter(words)
print(counts.most_common(3))
For grouping rather than counting, setdefault is the one-liner:
by_city = {}
for person in people:
by_city.setdefault(person["city"], []).append(person["name"])
The try/except alternative#
try:
value = person["email"]
except KeyError:
value = "not provided"
This is valid and, in a loop where the key is almost always present, marginally faster — Python exceptions are cheap to set up and expensive only when raised. For readability, most people reach for .get() instead. Use try/except when several lines inside the block might fail, not for a single lookup.
Nested dictionaries#
config = {"database": {"host": "localhost"}}
# Verbose but explicit
if "database" in config and "port" in config["database"]:
port = config["database"]["port"]
# Chained get with a fallback at each level
port = config.get("database", {}).get("port", 5432)
The empty dictionary as the first fallback is what stops the second .get() from failing when database is missing entirely.
Why this is fast#
big = {i: i * 2 for i in range(1_000_000)}
999_999 in big # about as fast as on a ten-item dictionary
Dictionaries are hash tables. Checking a key computes one hash and looks in one place, rather than walking the contents. The same check on a list of a million items compares up to a million values. If you have a membership test inside a loop and your data is in a list, converting it to a set or dictionary first is usually the biggest speed-up available.
Questions people ask#
Does in check keys or values?
Keys. For values use value in d.values(), though be aware that is a linear scan and much slower on large dictionaries.
Is if key in d faster than try/except?
When the key is usually missing, yes — raising an exception costs more than a lookup. When the key is almost always present, try/except edges ahead. The difference rarely matters; write whichever is clearer.
Can I check for a key in a nested dictionary in one call?
Not with a built-in. Chain .get() calls with {} defaults, or write a small helper that splits a dotted path and walks it level by level.
What types can be dictionary keys?
Anything hashable: strings, numbers, tuples of hashables, frozensets. Lists and dictionaries cannot be keys because they can change, which would break the hash table.
Where to go next#
- Python dictionaries explained — the full set of methods and when each applies.
- Python try/except explained — for the KeyError alternative above.
- Python lists explained — and why membership tests on them are slow.