First, key in d.keys() is guaranteed to give you the same value as key in d for any dict d.

And the in operation on a dict, or the dict_keys object you get back from calling keys() on it (in 3.x), is not O(N), it's O(1).

There's no real "optimization" going on; it's just that using the hash is the obvious way to implement __contains__ on a hash table, just as it's the obvious way to implement __getitem__.


You may ask where this is guaranteed.

Well, it's not. Mapping Types defines dict as, basically, a hash table implementation of collections.abc.Mapping. There's nothing stopping someone from creating a hash table implementation of a Mapping, but still providing O(N) searches. But it would be extra work to make such a bad implementation, so why would they?

If you really need to prove it to yourself, you can test every implementation you care about (with a profiler, or by using some type with a custom __hash__ and __eq__ that logs calls, or…), or read the source.


In 2.x, you do not want to call keys, because that generates a list of the keys, instead of a KeysView. You could use iterkeys, but that may generate an iterator or something else that's not O(1). So, just use the dict itself as a sequence.

Even in 3.x, you don't want to call keys, because there's no need to. Iterating a dict, checking its __contains__, and in general treating it like a sequence is always equivalent to doing the same thing to its keys, so why bother? (And of course building the trivial KeyView, and accessing through it, are going to add a few nanoseconds to your running time and a few keystrokes to your program.)

(It's not quite clear that using sequence operations is equivalent for d.keys()/d.iterkeys() and d in 2.x. Other than performance issues, they are equivalent in every CPython, Jython, IronPython, and PyPy implementation, but it doesn't seem to be stated anywhere the way it is in 3.x. And it doesn't matter; just use key in d.)


While we're at it, note that this:

if(dict[key] != None):

… is not going to work. If the key is not in the dict, this will raise KeyError, not return None.

Also, you should never check None with == or !=; always use is.

You can do this with a try—or, more simply, do if dict.get(key, None) is not None. But again, there's no reason to do so. Also, that won't handle cases where None is a perfectly valid item. If that's the case, you need to do something like sentinel = object(); if dict.get(key, sentinel) is not sentinel:.


So, the right thing to write is:

if key in d:

More generally, this is not true:

I know the "in" keyword is generally O(n) (as this just translates to python iterating over an entire list and comparing each element

The in operator, like most other operators, is just a call to a __contains__ method (or the equivalent for a C/Java/.NET/RPython builtin). list implements it by iterating the list and comparing each element; dict implements it by hashing the value and looking up the hash; blist.blist implements it by walking a B+Tree; etc. So, it could be O(n), O(1), O(log n), or something completely different.

Answer from abarnert on Stack Overflow
🌐
GeeksforGeeks
geeksforgeeks.org › python › time-complexities-of-python-dictionary
Time Complexities of Python Dictionary - GeeksforGeeks
July 23, 2025 - Removing an element by its key also has an average time complexity of O(1) using the same hash table mechanism. ... The 'in' keyword allows us to check if a key exists in the dictionary with an average complexity of O(1).
Top answer
1 of 4
163

First, key in d.keys() is guaranteed to give you the same value as key in d for any dict d.

And the in operation on a dict, or the dict_keys object you get back from calling keys() on it (in 3.x), is not O(N), it's O(1).

There's no real "optimization" going on; it's just that using the hash is the obvious way to implement __contains__ on a hash table, just as it's the obvious way to implement __getitem__.


You may ask where this is guaranteed.

Well, it's not. Mapping Types defines dict as, basically, a hash table implementation of collections.abc.Mapping. There's nothing stopping someone from creating a hash table implementation of a Mapping, but still providing O(N) searches. But it would be extra work to make such a bad implementation, so why would they?

If you really need to prove it to yourself, you can test every implementation you care about (with a profiler, or by using some type with a custom __hash__ and __eq__ that logs calls, or…), or read the source.


In 2.x, you do not want to call keys, because that generates a list of the keys, instead of a KeysView. You could use iterkeys, but that may generate an iterator or something else that's not O(1). So, just use the dict itself as a sequence.

Even in 3.x, you don't want to call keys, because there's no need to. Iterating a dict, checking its __contains__, and in general treating it like a sequence is always equivalent to doing the same thing to its keys, so why bother? (And of course building the trivial KeyView, and accessing through it, are going to add a few nanoseconds to your running time and a few keystrokes to your program.)

(It's not quite clear that using sequence operations is equivalent for d.keys()/d.iterkeys() and d in 2.x. Other than performance issues, they are equivalent in every CPython, Jython, IronPython, and PyPy implementation, but it doesn't seem to be stated anywhere the way it is in 3.x. And it doesn't matter; just use key in d.)


While we're at it, note that this:

if(dict[key] != None):

… is not going to work. If the key is not in the dict, this will raise KeyError, not return None.

Also, you should never check None with == or !=; always use is.

You can do this with a try—or, more simply, do if dict.get(key, None) is not None. But again, there's no reason to do so. Also, that won't handle cases where None is a perfectly valid item. If that's the case, you need to do something like sentinel = object(); if dict.get(key, sentinel) is not sentinel:.


So, the right thing to write is:

if key in d:

More generally, this is not true:

I know the "in" keyword is generally O(n) (as this just translates to python iterating over an entire list and comparing each element

The in operator, like most other operators, is just a call to a __contains__ method (or the equivalent for a C/Java/.NET/RPython builtin). list implements it by iterating the list and comparing each element; dict implements it by hashing the value and looking up the hash; blist.blist implements it by walking a B+Tree; etc. So, it could be O(n), O(1), O(log n), or something completely different.

2 of 4
18

In Python 2 dict.keys() creates the whole list of keys first that's why it is an O(N) operation, while key in dict is an O(1) operation.

if(dict[key] != None) will raise KeyError if key is not found in the dict, so it is not equivalent to the first code.

Python 2 results:

>>> dic = dict.fromkeys(range(10**5))
>>> %timeit 10000 in dic
1000000 loops, best of 3: 170 ns per loop
>>> %timeit 10000 in dic.keys()
100 loops, best of 3: 4.98 ms per loop
>>> %timeit 10000 in dic.iterkeys()
1000 loops, best of 3: 402 us per loop
>>> %timeit 10000 in dic.viewkeys()
1000000 loops, best of 3: 457 ns per loop

In Python 3 dict.keys() returns a view object which is quite faster than Python 2's keys() but still slower simple normal key in dict:

Python 3 results:

>>> dic = dict.fromkeys(range(10**5))
>>> %timeit 10000 in dic
1000000 loops, best of 3: 295 ns per loop
>>> %timeit 10000 in dic.keys()
1000000 loops, best of 3: 475 ns per loop

Use just:

if key in dict:
   #code
🌐
Pythondictionary
pythondictionary.org › home › dictionary methods
Python Dictionary Methods Reference | PythonDictionary.org | PythonDictionary.org
April 25, 2025 - person = {'name': 'John', 'age': 30, 'city': 'New York'} # Check if key exists if 'name' in person: print("Name found:", person['name']) # Check if key doesn't exist if 'email' not in person: print("Email not found") Time Complexity: O(1) average case · Use get() method instead of direct key ...
🌐
YouTube
youtube.com › codemake
check if key in dictionary python time complexity - YouTube
Download this code from https://codegive.com Certainly! Here's an informative tutorial about checking if a key exists in a dictionary in Python, along with a...
Published   December 20, 2023
Views   35
🌐
GeeksforGeeks
geeksforgeeks.org › python-test-if-element-is-part-of-dictionary
Python - Test if element is part of dictionary - GeeksforGeeks
May 2, 2023 - : " + str(res)) ... Time complexity: O(1), which is constant time. This is because dictionary in Python is implemented as a hash table, and the "in" operator uses hash table lookups to find the key, which takes constant time on average.
Find elsewhere
🌐
CodeGenes
codegenes.net › blog › how-to-check-if-key-exists-in-dict-python
How to Check if a Key Exists in a Dictionary in Python — codegenes.net
Use the in operator: The in operator is the most Pythonic and efficient way to check if a key exists in a dictionary. It has a time complexity of O(1) on average, which means it is very fast even for large dictionaries.
🌐
CodeGenes
codegenes.net › blog › how-to-check-if-key-is-in-dictionary-python
How to Check if a Key is in a Dictionary in Python — codegenes.net
# Create a dictionary available_books ... is the most straightforward and efficient method. It has a time complexity of O(1) on average, which means it can perform the check very quickly even for large dictionarie...
🌐
Codemia
codemia.io › knowledge-hub › path › check_if_a_given_key_already_exists_in_a_dictionary
Check if a given key already exists in a dictionary
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises
🌐
pythontutorials
pythontutorials.net › blog › how-can-i-check-if-a-key-exists-in-a-dictionary
How to Check if a Key Exists in a Python Dictionary: Simple Methods Explained — pythontutorials.net
Fast (O(1) time complexity). Works for all Python versions. Does not return the key’s value (you’ll need a separate lookup if you need the value). The dict.get(key, default) method retrieves the value associated with key if it exists; otherwise, ...
🌐
DEV Community
dev.to › ajipelumi › how-dictionary-lookup-operations-are-o1-49pk
How Dictionary Lookup Operations Are O(1) - DEV Community
September 5, 2024 - if key in dictionary – The Fast, ... to check, thanks to the hashing system we talked about earlier. This lookup is done in O(1) time, meaning it is super fast....
🌐
A Girl Among Geeks
agirlamonggeeks.com › home › how can you check if a key exists in a python dictionary?
How Can You Check If a Key Exists in a Python Dictionary?
July 4, 2025 - Does checking for a key in a dictionary have performance implications? Checking key existence using `in` is highly efficient, with average time complexity of O(1) due to the underlying hash table implementation. In Python, checking if a key exists in a dictionary is a fundamental operation ...
🌐
GeeksforGeeks
geeksforgeeks.org › python › python-check-given-key-already-exists-in-a-dictionary
Check if a Key Exists in a Python Dictionary - GeeksforGeeks
July 11, 2025 - With the Inbuilt method keys(), use the if statement with the 'in' operator to check if the key is present in the dictionary or not. ... # Python3 Program to check whether a # given key already exists in a dictionary.
🌐
OneUptime
oneuptime.com › home › blog › how to check if key exists in dictionary in python
How to Check if Key Exists in Dictionary in Python
January 25, 2026 - Dictionary key lookup is O(1) on average. import timeit data = {str(i): i for i in range(10000)} # All these are effectively O(1) and very fast # 'in' operator t1 = timeit.timeit(lambda: '5000' in data, number=100000) # get() method t2 = ...
🌐
Stack Overflow
stackoverflow.com › questions › 56087382 › what-is-the-time-complexity-of-searching-through-dictionary-key-values-while-usi › 56087432
python - What is the time complexity of searching through dictionary key values while using the key work *in* - Stack Overflow
When writing the code I was thinking that nums[i] in cache.keys() would take O(n) time to find nums[i] in the list of keys thus making the time complexity O(n^2). But the results of my solution look like its getting around O(n) time. This leads me to believe that nums[i] in cache.keys() is taking O(1) time. I was wondering if this is correct, and if someone could explain how this is happening. def twoSum(self, nums, target): cache = {} for i in range(len(nums)): b = target - nums[i] if nums[i] in cache.keys(): return [i, cache[nums[i]]] cache[b] = i; ... Dictionaries are implemented as hash tables/maps, which have average O(1) performance for look ups.