In Python, checking whether a key exists in a dictionary is a common operation. But there’s more than one way to write it:
if key in my_dict: ...if key in my_dict.keys(): ...
At first glance, both seem correct and equivalent. But they are not equal in performance or clarity.
In this post, we’ll dive into:
The internal differences between the two
The disassembled bytecode
Real-world benchmarks using timeit
Best practices and when it actually matters
Let’s compare the bytecode of two functions, one using in dict and the other using in dict.keys() to see what Python does under the hood.
import disdef direct_lookup(hmap, x):return x in hmapdef keys_lookup(hmap, x):return x in hmap.keys()dis.dis(direct_lookup)print("-"*40)dis.dis(keys_lookup)
Directly uses COMPARE_OP with in on the dictionary
Calls dict.__contains__, which is optimized and fast
x in hmap.keys()
Calls .keys() → creates a dict_keys view object
Performs a membership test on the view
This adds extra function calls and memory allocation, even though the lookup is still O(1).
Let’s use timeit to compare the performance of both lookup styles over 100,000 iterations.
import timeitsetup ="hmap = dict(zip(range(10000), range(10000))); x = 9999"stmt1 ="x in hmap"stmt2 ="x in hmap.keys()"time_direct = timeit.timeit(stmt1, setup=setup, number=100_000)time_keys = timeit.timeit(stmt2, setup=setup, number=100_000)print(f"'x in hmap' : {time_direct:.5f} sec")print(f"'x in hmap.keys()': {time_keys:.5f} sec")
'x in hmap' : 0.00363 sec
'x in hmap.keys()': 0.00700 sec
Summary
Style
Performance
Memory
x in dict
Fast
Low
x in dict.keys()
Slower
Higher
When to Use .keys()
Use .keys() only when you need to iterate over or manipulate the entire view object — not for simple membership tests.
Why It Matters
While the difference is small in absolute terms, understanding Python internals helps write clearer and more efficient code. It also prepares you to debug or optimize larger systems more confidently.