I tried to search the keys in a dictionary, but I forgot to add the keys()
function. I still got the expected answer.
Why is the result the same for these t
To understand why key in dct
returns the same result as key in dct.keys()
one needs to look in the past. Historically in Python 2, one would test the existence of a key in dictionary dct
with dct.has_key(key). This was changed for Python 2.2, when the preferred way became key in dct
, which basically did the same thing:
In a minor related change, the
in
operator now works on dictionaries, sokey in dict
is now equivalent todict.has_key(key)
The behaviour of in
is implemented internally in terms of the __contains__
dunder method. Its behaviour is documented in the Python language reference - 3 Data Model:
object.__contains__(self, item)
Called to implement membership test operators. Should return true if item is in
self
, false otherwise. For mapping objects, this should consider the keys of the mapping rather than the values or the key-item pairs. For objects that don’t define__contains__()
, the membership test first tries iteration via__iter__()
, then the old sequence iteration protocol via__getitem__()
, see this section in the language reference.
(emphasis mine; dictionaries in Python are mapping objects)
In Python 3, the has_key
method was removed altogether and now there the correct way to test for the existence of a key is solely key in dict
, as documented.
In contrast with the 2 above, key in dct.keys()
has never been the correct way of testing whether a key exists in a dictionary.
The result of both your examples is indeed the same, however key in dct.keys()
is slightly slower on Python 3 and is abysmally slow on Python 2.
key in dct
returns true, if the key
is found as a key in the dct
in almost constant time operation - it does not matter whether there are two or a million keys - its time complexity is constant on average case (O(1))
dct.keys()
in Python 2 creates a list
of all keys; and in Python 3 a view of keys; both of these objects understand the key in x
. With Python 2 it works like for any iterable; the values are iterated over and True
is returned as soon as one value is equal to the given value (here key
).
In practice, in Python 2 you'd find key in dct.keys()
much slower than key in dict
(key in dct.keys()
scales linearly with the number of keys - its time complexity is O(n) - both dct.keys()
, which builds a list of all keys, and key in key_list are O(n))
In Python 3, the key in dct.keys()
won't be much slower than key in dct
as the view does not make a list of the keys, and the access still would be O(1), however in practice it would be slower by at least a constant value, and it is 7 more characters, so there is usually practically no reason to use it, even if on Python 3.
Python data model dictates that generally a membership test are normally implemented as an iteration through a sequence unless a container object supplies the special method __contains__ .
As mentioned further in the document, for objects that does not implement the __contains__ special method, the membership test first tries iteration via __iter__()
, then the old sequence iteration protocol via __getitem__()
.
Its important to know that for dictionaries, dict.keys()
returns either an iterator either a dictionary view (Python 3.X) or a sequence (more precisely a list), in Python (2.X). Membership test for a sequence/list is an O(n)
complexity where as for a dictionary like object which is implemented as a hash map, or a dictionary view which supports operation like supports operations like membership test and iteration has a complexity of O(1).
So for Python 2.X, there is a distinct difference in terms of what both does, that might impact performance, where as for Python 2.X, the only overhead is an extra function call.
In any case, it is always preferred to use the membership on the dict object rather than using the membership test on a dictionary view or a sequence which is returned by dict.keys