My answer is «neither one».
I believe the most «Pythonic» way to do things is to NOT check beforehand if the key is in a dictionary and instead just write code that assumes it’s there and catch any KeyErrors that get raised because it wasn’t.
This is usually done with enclosing the code in a try...except
clause and is a well-known idiom usually expressed as «It’s easier to ask forgiveness than permission» or with the acronym EAFP, which basically means it is better to try something and catch the errors instead for making sure everything’s OK before doing anything. Why validate what doesn’t need to be validated when you can handle exceptions gracefully instead of trying to avoid them? Because it’s often more readable and the code tends to be faster if the probability is low that the key won’t be there (or whatever preconditions there may be).
Of course, this isn’t appropriate in all situations and not everyone agrees with the philosophy, so you’ll need to decide for yourself on a case-by-case basis. Not surprisingly the opposite of this is called LBYL for «Look Before You Leap».
As a trivial example consider:
if 'name' in dct:
value = dct['name'] * 3
else:
logerror('"%s" not found in dictionary, using default' % name)
value = 42
vs
try:
value = dct['name'] * 3
except KeyError:
logerror('"%s" not found in dictionary, using default' % name)
value = 42
Although in the case it’s almost exactly the same amount of code, the second doesn’t spend time checking first and is probably slightly faster because of it (try…except block isn’t totally free though, so it probably doesn’t make that much difference here).
Generally speaking, testing in advance can often be much more involved and the savings gain from not doing it can be significant. That said, if 'name' in dict:
is better for the reasons stated in the other answers.
If you’re interested in the topic, this message titled «EAFP vs LBYL (was Re: A little disappointed so far)» from the Python mailing list archive probably explains the difference between the two approached better than I have here. There’s also a good discussion about the two approaches in the book Python in a Nutshell, 2nd Ed by Alex Martelli in chapter 6 on Exceptions titled Error-Checking Strategies. (I see there’s now a newer 3rd edition, publish in 2017, which covers both Python 2.7 and 3.x).
In this tutorial, you’ll learn how to use Python to check if a key exists in a dictionary. You’ll also learn how to check if a value exists in a dictionary. You’ll learn how to do this using the in
operator, the .get()
method, the has_key()
function, and the .keys()
and .values()
methods.
Knowing how to work with Python dictionaries is an important skill. This can be especially helpful when working with web APIs that return JSON data.
While we can easily index a dictionary, if a key doesn’t exist, a KeyError
will be thrown. This will cause some significant problems in your program, unless these errors are handled.
A much more safe alternative to using dictionary indexing, is to first check if a given key exists in a dictionary. Let’s get started learning!
The Quick Answer: Use in
to see if a key exists
What is a Python Dictionary?
Dictionaries in Python are one of the main, built-in data structures. They consist of key:value
pairs that make finding an items value easy, if you know their corresponding key. One of the unique attributes of a dictionary is that keys must be unique, but that values can be duplicated.
Let’s take a look at how dictionaries look in Python. They’re created using {}
curly brackets and the key:value
pairs are separated by commas.
Let’s create a dictionary called ages
, which, well, contains the ages of different people:
ages = {
'Matt': 30,
'Katie': 29,
'Nik': 31,
'Jack': 43,
'Alison': 32,
'Kevin': 38
}
One way that you’re often taught to access a dictionary value is to use indexing via the []
square bracket accessing. In the next section, you’ll see how dictionary indexing works and why it’s not always the best option. Following that, you’ll learn different methods of ensuring that a key exists.
The Problem with Indexing a Python Dictionary
Indexing a dictionary is an easy way of getting a dictionary key’s value – if the given key exists in the dictionary. Let’s take a look at how dictionary indexing works. We’ll use dictionary indexing to get the value for the key Nik
from our dictionary ages
:
>>> ages = {'Matt': 30, 'Katie': 29, 'Nik': 31, 'Jack': 43, 'Alison': 32, 'Kevin': 38}
>>> print(ages['Nik'])
31
We can see here that this worked beautifully. That being said, let’s see if we try to get the value for the key Jill
, which doesn’t exist in the dictionary:
>>> ages = {'Matt': 30, 'Katie': 29, 'Nik': 31, 'Jack': 43, 'Alison': 32, 'Kevin': 38}
>>> print(ages['Jill'])
KeyError: 'Jill'
We can see here, that if we try to access a dictionary’s value for a key that doesn’t exist, that a KeyError
is thrown. This has some huge implications for your code. Unless the error is explicitly handled, the program will fail. One way to avoid a KeyError
is to ensure that a key actually exists in a Python dictionary.
That’s exactly what you’ll learn in the next few sections. Let’s get started!
Use Python to Check if a Key Exists: Python keys Method
Python dictionary come with a built-in method that allows us to generate a list-like object that contains all the keys in a dictionary. Conveniently, this is named the .keys()
method.
Printing out dict.keys()
looks like this:
print(ages.keys())
# Returns: dict_keys(['Matt', 'Katie', 'Nik', 'Jack', 'Alison', 'Kevin'])
We can see how that looks like a little bit like a list. We can now check if a key exists in that list-like object!
Let’s see how we can use the .keys()
method to see if a key exists in a dictionary. Let’s use this method to see if a key exists:
# Check if a key exists in a Python dictionary by checking all keys
ages = {'Matt': 30, 'Katie': 29, 'Nik': 31, 'Jack': 43, 'Alison': 32, 'Kevin': 38}
some_key = 'James'
if some_key in ages.keys():
print('Key exists')
else:
print('Key doesn't exist')
# Returns Key doesn't exist
We can see here that we check whether or not a provided key, some_key
, exists in the keys of our dictionary. In this case, the key didn’t exist and the program printed out Key doesn't exist
.
In the next section, you’ll learn how to simplify this even further!
Use Python to Check if a Key Exists: Python in Operator
The method above works well, but we can simplify checking if a given key exists in a Python dictionary even further. We can actually omit the .keys()
method entirely, and using the in
operator will scan all keys in a dictionary.
Let’s see how this works in practise:
ages = {'Matt': 30, 'Katie': 29, 'Nik': 31, 'Jack': 43, 'Alison': 32, 'Kevin': 38}
some_key = 'Nik'
if some_key in ages:
print('Key exists')
else:
print('Key doesn't exist')
# Returns: Key exists
We can see here that our method actually looks very similar to the above, but we’ve been able to strip out the .keys()
method entirely! This actually helps the code read a bit more plain-language.
in the next section, you’ll learn how to actually retrieve a key’s value, even if a key doesn’t exist!
Check out some other Python tutorials on datagy, including our complete guide to styling Pandas and our comprehensive overview of Pivot Tables in Pandas!
Use the .get Method to Check if a Key Exists in a Python Dictionary
Working with dictionaries in Python generally involves getting a key’s value – not just checking if it exists. You learned earlier that simply indexing the dictionary will throw a KeyError
if a key doesn’t exist. How can we do this safely, without breaking out program?
The answer to this, is to use the Python .get()
method. The .get()
method will simply return None
if a key doesn’t exist. Let’s try this out:
ages = {'Matt': 30, 'Katie': 29, 'Nik': 31, 'Jack': 43, 'Alison': 32, 'Kevin': 38}
print(ages.get('Jack'))
print(ages.get('Jill'))
# Returns:
# 43
# None
We can see here that when the .get()
method is applied to return a key that exists, that key’s value is correctly returned. When a key doesn’t exist, the program continues to run, but it returns None
.
What is particularly helpful about the Python .get()
method, is that it allows us to return a value, even if a key doesn’t exist.
Say we wanted our program to notify us that a key didn’t exist. We could ask the .get()
method to return “Key doesn’t exist!”.
Let’s see how we can do this:
ages = {'Matt': 30, 'Katie': 29, 'Nik': 31, 'Jack': 43, 'Alison': 32, 'Kevin': 38}
print(ages.get('Jill', "The key doesn't exist"))
# Returns: "The key doesn't exist!"
The .get()
method is a great and safe way to see if a key exists in a Python dictionary. Now, let’s learn to see whether or not a given value exists in a Python dictionary.
Similar to the Python dictionary .keys()
method, dictionaries have a corresponding .values()
method, which returns a list-like object for all the values in a dictionary.
Let’s see how we can access all of a dictionary’s values:
ages = {'Matt': 30, 'Katie': 29, 'Nik': 31, 'Jack': 43, 'Alison': 32, 'Kevin': 38}
print(ages.values())
#Returns: dict_values([30, 29, 31, 43, 32, 38])
We can use this to see whether or not a value exists. Say we wanted to know if the age 27 existed in our dictionary, we could write the following:
ages = {'Matt': 30, 'Katie': 29, 'Nik': 31, 'Jack': 43, 'Alison': 32, 'Kevin': 38}
some_age = 27
if some_age in ages.values():
print('Age exists!')
else:
print("Age doesn't exist!")
# Returns: Age doesn't exist!
Now, what if we wanted to return the key or keys for a given value. We can safely do this using a list comprehension, which will have three permutations:
- Be an empty list, if the value doesn’t exist,
- Have one item, if the value exists once
- Have more than one item, if the value exists more than once
Let’s use a slightly modified dictionary to see this in action:
# Getting all keys of a certain value in a Python dictionary
ages = {'Matt': 30, 'Katie': 29, 'Nik': 31, 'Jack': 43, 'Jill': 43, 'Alison': 32, 'Kevin': 38}
value_43 = [key for key, value in ages.items() if value == 43]
print(value_43)
# Returns: ['Jack', 'Jill']
We’ve created a list comprehension that adds each key, if the value of that key is equal to 43.
What are Python List Comprehensions? To learn more about list comprehensions, check out my comprehensive tutorial here and my in-depth video below!
Conclusion
In this post, you learned how to check if a key exists in a Python dictionary. You learned how to do this with the .keys()
method, the in
operator, and the .get()
method. You also learned how to see if a given value exists in a Python dictionary and how to get that values’ key(s).
To learn more about dictionaries in Python, check out the official documentation here.
In this article we will discuss six different ways to check if key exists in a Dictionary in Python.
Table of Contents
Suppose we have a dictionary of string and int i.e.
# Dictionary of string and int word_freq = { "Hello": 56, "at": 23, "test": 43, "this": 78 }
Now we want to check if key ‘test’ exist in this dictionary or not.
There are different ways to do this. Let’s discuss them one by one.
Check if key in Python Dictionary using if-in statement
We can directly use the ‘in operator’ with the dictionary to check if a key exist in dictionary or nor. The expression,
Advertisements
key in dictionary
Will evaluate to a boolean value and if key exist in dictionary then it will evaluate to True, otherwise False. Let’s use this to check if key is in dictionary or not. For example,
# Dictionary of string and int word_freq = { "Hello": 56, "at": 23, "test": 43, "this": 78 } key = 'test' # python check if key in dict using "in" if key in word_freq: print(f"Yes, key: '{key}' exists in dictionary") else: print(f"No, key: '{key}' does not exists in dictionary")
Output:
Yes, key: 'test' exists in dictionary
Here it confirms that the key ‘test’ exist in the dictionary.
Now let’s test a negative example i.e. check if key ‘sample’ exist in the dictionary or not i.e.
# Dictionary of string and int word_freq = { "Hello": 56, "at": 23, "test": 43, "this": 78 } key = 'sample' # python check if key in dict using "in" if key in word_freq: print(f"Yes, key: '{key}' exists in dictionary") else: print(f"No, key: '{key}' does not exists in dictionary")
Output:
No, key: 'sample' does not exists in dictionary
Here it confirms that the key ‘sample’ does not exist in the dictionary.
Check if Dictionary has key using get() function
In python, the dict class provides a method get() that accepts a key and a default value i.e.
dict.get(key[, default])
Behavior of this function,
- If given key exists in the dictionary, then it returns the value associated with this key,
- If given key does not exists in dictionary, then it returns the passed default value argument.
- If given key does not exists in dictionary and Default value is also not provided, then it returns None.
Let’s use get() function to check if given key exists in dictionary or not,
# Dictionary of string and int word_freq = { "Hello": 56, "at": 23, "test": 43, "this": 78 } key = 'sample' # check if key exists in dictionary by checking if get() returned None if word_freq.get(key) is not None: print(f"Yes, key: '{key}' exists in dictionary") else: print(f"No, key: '{key}' does not exists in dictionary")
Output:
No, key: 'sample' does not exists in dictionary
Here it confirmed that the key ‘sample’ does not exist in the dictionary.
We passed ‘sample’ argument in the get() function, without any default value. As our dictionary does not contain ant key ‘sample’ and no default value is provided, therefore it returned None.
If we pass the default value along with the key and if key does not exist in the dictionary, then it returns the default value. For example,
key = 'sample' # check if key exists in dictionary by checking if get() returned default value if word_freq.get(key, -1) != -1: print(f"Yes, key: '{key}' exists in dictionary") else: print(f"No, key: '{key}' does not exists in dictionary")
Output:
No, key: 'sample' does not exists in dictionary
Here it confirmed that the key ‘sample’ does not exist in the dictionary.
We passed ‘sample’ argument in the get() function, along with the default value -1. As our dictionary does not contain ant key ‘sample’, so get() function returned the the default value.
We cannot always be sure with the result of dict.get(), that key exists in dictionary or not . Therefore, we should use dict.get() to check existence of key in dictionary only if we are sure that there cannot be an entry of key with given default value.
keys() function of the dictionary returns a sequence of all keys in the dictionary. So, we can use ‘in’ keyword with the returned sequence of keys to check if key exist in the dictionary or not. For example,
word_freq = { "Hello": 56, "at": 23, "test": 43, "this": 78 } key = 'test' if key in word_freq.keys(): print(f"Yes, key: '{key}' exists in dictionary") else: print(f"No, key: '{key}' does not exists in dictionary")
Output:
Yes, key: 'test' exists in dictionary
Here it confirms that the key ‘test’ exist in the dictionary.
Check if key in Python Dictionary using try/except
If we try to access the value of key that does not exist in the dictionary, then it will raise KeyError. This can also be a way to check if exist in dict or not i.e.
def check_key_exist(test_dict, key): try: value = test_dict[key] return True except KeyError: return False # Dictionary of string and int word_freq = { "Hello": 56, "at": 23, "test": 43, "this": 78 } key = 'test' # check if dictionary has key in python if check_key_exist(word_freq, key): print(f"Yes, key: '{key}' exists in dictionary") else: print(f"No, key: '{key}' does not exists in dictionary")
Output:
Yes, key: 'test' exists in dictionary
Here it confirms that the key ‘test’ exist in the dictionary.
In function check_key_exist(), it access the value of given key. If key does not exist then KeyError occurs, in that case it returns False, otherwise it returns True
Check if key not in Python Dictionary using ‘if not in’ statement
In all the above example, we checked if key exist in dictionary or not. But if want to check if key doesn’t exist in dictionary then we can directly use ‘not in’ with dictionary i.e.
word_freq = { "Hello": 56, "at": 23, "test": 43, "this": 78 } key = 'sample' # Check if key not in dict python if key not in word_freq: print(f"No, key: '{key}' does not exists in dictionary") else: print(f"Yes, key: '{key}' exists in dictionary")
Output:
No, key: 'sample' does not exists in dictionary
Here it confirms that the key ‘test’ exist in the dictionary.
Check if key exist in Python Dictionary using has_key()
dict provides a function has_key() to check if key exist in dictionary or not. But this function is discontinued in python 3. So, below example will run in python 2.7 only i.e.
if word_freq.has_key('test'): print("Yes 'test' key exists in dict") else: print("No 'test' key does not exists in dict")
Output:
Yes, key: 'test' exists in dictionary
Here it confirms that the key ‘test’ exist in the dictionary.
The complete example is as follows.
def check_key_exist(test_dict, key): try: value = test_dict[key] return True except KeyError: return False def main(): # Dictionary of string and int word_freq = { "Hello": 56, "at": 23, "test": 43, "this": 78 } print("*** Python: check if key in dictionary using if-in statement***") key = 'test' # python check if key in dict using "in" if key in word_freq: print(f"Yes, key: '{key}' exists in dictionary") else: print(f"No, key: '{key}' does not exists in dictionary") key = 'sample' # python check if key in dict using "in" if key in word_freq: print(f"Yes, key: '{key}' exists in dictionary") else: print(f"No, key: '{key}' does not exists in dictionary") print("*** Python: check if dict has key using get() function ***") key = 'sample' # check if key exists in dictionary by checking if get() returned None if word_freq.get(key) is not None: print(f"Yes, key: '{key}' exists in dictionary") else: print(f"No, key: '{key}' does not exists in dictionary") key = 'sample' # check if key exists in dictionary by checking if get() returned default value if word_freq.get(key, -1) != -1: print(f"Yes, key: '{key}' exists in dictionary") else: print(f"No, key: '{key}' does not exists in dictionary") print('python check if key in dict using keys()') key = 'test' if key in word_freq.keys(): print(f"Yes, key: '{key}' exists in dictionary") else: print(f"No, key: '{key}' does not exists in dictionary") print('python check if key in dict using try/except') print('python check if key in dictionary using try/except') key = 'test' # check if dictionary has key in python if check_key_exist(word_freq, key): print(f"Yes, key: '{key}' exists in dictionary") else: print(f"No, key: '{key}' does not exists in dictionary") print('check if key not in dictionary in python using if not in statement') key = 'sample' # Check if key not in dict python if key not in word_freq: print(f"No, key: '{key}' does not exists in dictionary") else: print(f"Yes, key: '{key}' exists in dictionary") print('check if key not in dictionary in python using has_keys') if __name__ == '__main__': main()
Output
*** Python: check if key in dictionary using if-in statement*** Yes, key: 'test' exists in dictionary No, key: 'sample' does not exists in dictionary *** Python: check if dict has key using get() function *** No, key: 'sample' does not exists in dictionary No, key: 'sample' does not exists in dictionary python check if key in dict using keys() Yes, key: 'test' exists in dictionary python check if key in dict using try/except python check if key in dictionary using try/except Yes, key: 'test' exists in dictionary check if key not in dictionary in python using if not in statement No, key: 'sample' does not exists in dictionary check if key not in dictionary in python using has_keys
Python’s in
and not in
operators allow you to quickly determine if a given value is or isn’t part of a collection of values. This type of check is common in programming, and it’s generally known as a membership test in Python. Therefore, these operators are known as membership operators.
In this tutorial, you’ll learn how to:
- Perform membership tests using the
in
andnot in
operators - Use
in
andnot in
with different data types - Work with
operator.contains()
, the equivalent function to thein
operator - Provide support for
in
andnot in
in your own classes
To get the most out of this tutorial, you’ll need basic knowledge of Python, including built-in data types, such as lists, tuples, ranges, strings, sets, and dictionaries. You’ll also need to know about Python generators, comprehensions, and classes.
Getting Started With Membership Tests in Python
Sometimes you need to find out whether a value is present in a collection of values or not. In other words, you need to check if a given value is or is not a member of a collection of values. This kind of check is commonly known as a membership test.
Arguably, the natural way to perform this kind of check is to iterate over the values and compare them with the target value. You can do this with the help of a for
loop and a conditional statement.
Consider the following is_member()
function:
>>>
>>> def is_member(value, iterable):
... for item in iterable:
... if value is item or value == item:
... return True
... return False
...
This function takes two arguments, the target value
and a collection of values, which is generically called iterable
. The loop iterates over iterable
while the conditional statement checks if the target value
is equal to the current value. Note that the condition checks for object identity with is
or for value equality with the equality operator (==
). These are slightly different but complementary tests.
If the condition is true, then the function returns True
, breaking out of the loop. This early return short-circuits the loop operation. If the loop finishes without any match, then the function returns False
:
>>>
>>> is_member(5, [2, 3, 5, 9, 7])
True
>>> is_member(8, [2, 3, 5, 9, 7])
False
The first call to is_member()
returns True
because the target value, 5
, is a member of the list at hand, [2, 3, 5, 9, 7]
. The second call to the function returns False
because 8
isn’t present in the input list of values.
Membership tests like the ones above are so common and useful in programming that Python has dedicated operators to perform these types of checks. You can get to know the membership operators in the following table:
Operator | Description | Syntax |
---|---|---|
in |
Returns True if the target value is present in a collection of values. Otherwise, it returns False . |
value in collection |
not in |
Returns True if the target value is not present in a given collection of values. Otherwise, it returns False . |
value not in collection |
As with Boolean operators, Python favors readability by using common English words instead of potentially confusing symbols as operators.
Like many other operators, in
and not in
are binary operators. That means you can create expressions by connecting two operands. In this case, those are:
- Left operand: The target value that you want to look for in a collection of values
- Right operand: The collection of values where the target value may be found
The syntax of a membership test looks something like this:
value in collection
value not in collection
In these expressions, value
can be any Python object. Meanwhile, collection
can be any data type that can hold collections of values, including lists, tuples, strings, sets, and dictionaries. It can also be a class that implements the .__contains__()
method or a user-defined class that explicitly supports membership tests or iteration.
If you use the in
and not in
operators correctly, then the expressions that you build with them will always evaluate to a Boolean value. In other words, those expressions will always return either True
or False
. On the other hand, if you try and find a value in something that doesn’t support membership tests, then you’ll get a TypeError
. Later, you’ll learn more about the Python data types that support membership tests.
Because membership operators always evaluate to a Boolean value, Python considers them Boolean operators just like the and
, or
, and not
operators.
Now that you know what membership operators are, it’s time to learn the basics of how they work.
Python’s in
Operator
To better understand the in
operator, you’ll start by writing some small demonstrative examples that determine if a given value is in a list:
>>>
>>> 5 in [2, 3, 5, 9, 7]
True
>>> 8 in [2, 3, 5, 9, 7]
False
The first expression returns True
because 5
appears inside your list of numbers. The second expression returns False
because 8
isn’t present in the list.
According to the in
operator documentation, an expression like value in collection
is equivalent to the following code:
any(value is item or value == item for item in collection)
The generator expression wrapped in the call to any()
builds a list of the Boolean values that result from checking if the target value
has the same identity or is equal to the current item
in collection
. The call to any()
checks if any one of the resulting Boolean values is True
, in which case the function returns True
. If all the values are False
, then any()
returns False
.
Python’s not in
Operator
The not in
membership operator does exactly the opposite. With this operator, you can check if a given value is not in a collection of values:
>>>
>>> 5 not in [2, 3, 5, 9, 7]
False
>>> 8 not in [2, 3, 5, 9, 7]
True
In the first example, you get False
because 5
is in [2, 3, 5, 9, 7]
. In the second example, you get True
because 8
isn’t in the list of values. This negative logic may seem like a tongue twister. To avoid confusion, remember that you’re trying to determine if the value is not part of a given collection of values.
With this quick overview of how membership operators work, you’re ready to go to the next level and learn how in
and not in
work with different built-in data types.
Using in
and not in
With Different Python Types
All built-in sequences—such as lists, tuples, range
objects, and strings—support membership tests with the in
and not in
operators. Collections like sets and dictionaries also support these tests. By default, membership operations on dictionaries check whether the dictionary has a given key or not. However, dictionaries also have explicit methods that allow you to use the membership operators with keys, values, and key-value pairs.
In the following sections, you’ll learn about a few particularities of using in
and not in
with different built-in data types. You’ll start with lists, tuples, and range
objects to kick things off.
Lists, Tuples, and Ranges
So far, you’ve coded a few examples of using the in
and not in
operators to determine if a given value is present in an existing list of values. For these examples, you’ve explicitly used list
objects. So, you’re already familiar with how membership tests work with lists.
With tuples, the membership operators work the same as they would with lists:
>>>
>>> 5 in (2, 3, 5, 9, 7)
True
>>> 5 not in (2, 3, 5, 9, 7)
False
There are no surprises here. Both examples work the same as the list-focused examples. In the first example, the in
operator returns True
because the target value, 5
, is in the tuple. In the second example, not in
returns the opposite result.
For lists and tuples, the membership operators use a search algorithm that iterates over the items in the underlying collection. Therefore, as your iterable gets longer, the search time increases in direct proportion. Using Big O notation, you’d say that membership operations on these data types have a time complexity of O(n).
If you use the in
and not in
operators with range
objects, then you get a similar result:
>>>
>>> 5 in range(10)
True
>>> 5 not in range(10)
False
>>> 5 in range(0, 10, 2)
False
>>> 5 not in range(0, 10, 2)
True
When it comes to range
objects, using membership tests may seem unnecessary at first glance. Most of the time, you’ll know the values in the resulting range beforehand. But what if you’re using range()
with offsets that are determined at runtime?
Consider the following examples, which use random numbers to determine offsets at runtime:
>>>
>>> from random import randint
>>> 50 in range(0, 100, randint(1, 10))
False
>>> 50 in range(0, 100, randint(1, 10))
False
>>> 50 in range(0, 100, randint(1, 10))
True
>>> 50 in range(0, 100, randint(1, 10))
True
On your machine, you might get different results because you’re working with random range offsets. In these specific examples, step
is the only offset that varies. In real code, you could have varying values for the start
and stop
offsets as well.
For range
objects, the algorithm behind the membership tests computes the presence of a given value using the expression (value - start) % step) == 0
, which depends on the offsets used to create the range at hand. This makes membership tests very efficient when they operate on range
objects. In this case, you’d say that their time complexity is O(1).
Remember that the target value in a membership test can be of any type. The test will check if that value is or isn’t in the target collection. For example, say that you have a hypothetical app where the users authenticate with a username and a password. You can have something like this:
# users.py
username = input("Username: ")
password = input("Password: ")
users = [("john", "secret"), ("jane", "secret"), ("linda", "secret")]
if (username, password) in users:
print(f"Hi {username}, you're logged in!")
else:
print("Wrong username or password")
This is a naive example. It’s unlikely that anyone would handle their users and passwords like this. But the example shows that the target value can be of any data type. In this case, you use a tuple of strings representing the username and the password of a given user.
Here’s how the code works in practice:
$ python users.py
Username: john
Password: secret
Hi john, you're logged in!
$ python users.py
Username: tina
Password: secret
Wrong username or password
In the first example, the username and password are correct because they’re in the users
list. In the second example, the username doesn’t belong to any registered user, so the authentication fails.
In these examples, it’s important to note that the order in which the data is stored in the login tuple is critical because something like ("john", "secret")
isn’t equal to ("secret", "john")
in tuple comparison even if they have the same items.
In this section, you’ve explored examples that showcase the core behavior of membership operators with common Python built-in sequences. However, there’s a built-in sequence left. Yes, strings! In the next section, you’ll learn how membership operators work with this data type in Python.
Strings
Python strings are a fundamental tool in every Python developer’s tool kit. Like tuples, lists, and ranges, strings are also sequences because their items or characters are sequentially stored in memory.
You can use the in
and not in
operators with strings when you need to figure out if a given character is present in the target string. For example, say that you’re using strings to set and manage user permissions for a given resource:
>>>
>>> class User:
... def __init__(self, username, permissions):
... self.username = username
... self.permissions = permissions
...
>>> admin = User("admin", "wrx")
>>> john = User("john", "rx")
>>> def has_permission(user, permission):
... return permission in user.permissions
...
>>> has_permission(admin, "w")
True
>>> has_permission(john, "w")
False
The User
class takes two arguments, a username and a set of permissions. To provide the permissions, you use a string in which w
means that the user has write permission, r
means that the user has read permission, and x
implies execution permissions. Note that these letters are the same ones that you’d find in the Unix-style file-system permissions.
The membership test inside has_permission()
checks whether the current user
has a given permission
or not, returning True
or False
accordingly. To do this, the in
operator searches the permissions string to find a single character. In this example, you want to know if the users have write permission.
However, your permission system has a hidden issue. What would happen if you called the function with an empty string? Here’s your answer:
>>>
>>> has_permission(john, "")
True
Because an empty string is always considered a substring of any other string, an expression like "" in user.permissions
will return True
. Depending on who has access to your users’ permissions, this behavior of membership tests may imply a security breach in your system.
You can also use the membership operators to determine if a string contains a substring:
>>>
>>> greeting = "Hi, welcome to Real Python!"
>>> "Hi" in greeting
True
>>> "Hi" not in greeting
False
>>> "Hello" in greeting
False
>>> "Hello" not in greeting
True
For the string data type, an expression like substring in string
is True
if substring
is part of string
. Otherwise, the expression is False
.
An important point to remember when using membership tests on strings is that string comparisons are case-sensitive:
>>>
>>> "PYTHON" in greeting
False
This membership test returns False
because strings comparisons are case-sensitive, and "PYTHON"
in uppercase isn’t present in greeting
. To work around this case sensitivity, you can normalize all your strings using either the .upper()
or .lower()
method:
>>>
>>> "PYTHON".lower() in greeting.lower()
True
In this example, you use .lower()
to convert the target substring and the original string into lowercase letters. This conversion tricks the case sensitivity in the implicit string comparison.
Generators
Generator functions and generator expressions create memory-efficient iterators known as generator iterators. To be memory efficient, these iterators yield items on demand without keeping a complete series of values in memory.
In practice, a generator function is a function that uses the yield
statement in its body. For example, say that you need a generator function that takes a list of numbers and returns an iterator that yields square values from the original data. In this case, you can do something like this:
>>>
>>> def squares_of(values):
... for value in values:
... yield value ** 2
...
>>> squares = squares_of([1, 2, 3, 4])
>>> next(squares)
1
>>> next(squares)
4
>>> next(squares)
9
>>> next(squares)
16
>>> next(squares)
Traceback (most recent call last):
...
StopIteration
This function returns a generator iterator that yields square numbers on demand. You can use the built-in next()
function to retrieve consecutive values from the iterator. When the generator iterator is completely consumed, it raises a StopIteration
exception to communicate that no more values are left.
You can use the membership operators on a generator function like squares_of()
:
>>>
>>> 4 in squares_of([1, 2, 3, 4])
True
>>> 9 in squares_of([1, 2, 3, 4])
True
>>> 5 in squares_of([1, 2, 3, 4])
False
The in
operator works as expected when you use it with generator iterators, returning True
if the value is present in the iterator and False
otherwise.
However, there’s something you need to be aware of when checking for membership on generators. A generator iterator will yield each item only once. If you consume all the items, then the iterator will be exhausted, and you won’t be able to iterate over it again. If you consume only some items from a generator iterator, then you can iterate over the remaining items only.
When you use in
or not in
on a generator iterator, the operator will consume it while searching for the target value. If the value is present, then the operator will consume all the values up to the target value. The rest of the values will still be available in the generator iterator:
>>>
>>> squares = squares_of([1, 2, 3, 4])
>>> 4 in squares
True
>>> next(squares)
9
>>> next(squares)
16
>>> next(squares)
Traceback (most recent call last):
...
StopIteration
In this example, 4
is in the generator iterator because it’s the square of 2
. Therefore, in
returns True
. When you use next()
to retrieve a value from square
, you get 9
, which is the square of 3
. This result confirms that you no longer have access to the first two values. You can continue calling next()
until you get a StopIteration
exception when the generator iterator is exhausted.
Likewise, if the value isn’t present in the generator iterator, then the operator will consume the iterator completely, and you won’t have access to any of its values:
>>>
>>> squares = squares_of([1, 2, 3, 4])
>>> 5 in squares
False
>>> next(squares)
Traceback (most recent call last):
...
StopIteration
In this example, the in
operator consumes squares
completely, returning False
because the target value isn’t in the input data. Because the generator iterator is now exhausted, a call to next()
with squares
as an argument raises StopIteration
.
You can also create generator iterators using generator expressions. These expressions use the same syntax as list comprehensions but replace the square brackets ([]
) with round brackets (()
). You can use the in
and not in
operators with the result of a generator expression:
>>>
>>> squares = (value ** 2 for value in [1, 2, 3, 4])
>>> squares
<generator object <genexpr> at 0x1056f20a0>
>>> 4 in squares
True
>>> next(squares)
9
>>> next(squares)
16
>>> next(squares)
Traceback (most recent call last):
...
StopIteration
The squares
variable now holds the iterator that results from the generator expression. This iterator yields square values from the input list of numbers. Generator iterators from generator expressions work the same as generator iterators from generator functions. So, the same rules apply when you use them in membership tests.
Another critical issue can arise when you use the in
and not in
operators with generator iterators. This issue can appear when you’re working with infinite iterators. The function below returns an iterator that yields infinite integers:
>>>
>>> def infinite_integers():
... number = 0
... while True:
... yield number
... number += 1
...
>>> integers = infinite_integers()
>>> integers
<generator object infinite_integers at 0x1057e8c80>
>>> next(integers)
0
>>> next(integers)
1
>>> next(integers)
2
>>> next(integers)
3
>>> next(integers)
The infinite_integers()
function returns a generator iterator, which is stored in integers
. This iterator yields values on demand, but remember, there will be infinite values. Because of this, it won’t be a good idea to use the membership operators with this iterator. Why? Well, if the target value isn’t in the generator iterator, then you’ll run into an infinite loop that’ll make your execution hang.
Dictionaries and Sets
Python’s membership operators also work with dictionaries and sets. If you use the in
or not in
operators directly on a dictionary, then it’ll check whether the dictionary has a given key or not. You can also do this check using the .keys()
method, which is more explicit about your intentions.
You can also check if a given value or key-value pair is in a dictionary. To do these checks, you can use the .values()
and .items()
methods, respectively:
>>>
>>> likes = {"color": "blue", "fruit": "apple", "pet": "dog"}
>>> "fruit" in likes
True
>>> "hobby" in likes
False
>>> "blue" in likes
False
>>> "fruit" in likes.keys()
True
>>> "hobby" in likes.keys()
False
>>> "blue" in likes.keys()
False
>>> "dog" in likes.values()
True
>>> "drawing" in likes.values()
False
>>> ("color", "blue") in likes.items()
True
>>> ("hobby", "drawing") in likes.items()
False
In these examples, you use the in
operator directly on your likes
dictionary to check whether the "fruit"
, "hobby"
, and "blue"
keys are in the dictionary or not. Note that even though "blue"
is a value in likes
, the test returns False
because it only considers the keys.
Next up, you use the .keys()
method to get the same results. In this case, the explicit method name makes your intentions much clearer to other programmers reading your code.
To check if a value like "dog"
or "drawing"
is present in likes
, you use the .values()
method, which returns a view object with the values in the underlying dictionary. Similarly, to check if a key-value pair is contained in likes
, you use .items()
. Note that the target key-value pairs must be two-item tuples with the key and value in that order.
If you’re using sets, then the membership operators work as they would with lists or tuples:
>>>
>>> fruits = {"apple", "banana", "cherry", "orange"}
>>> "banana" in fruits
True
>>> "banana" not in fruits
False
>>> "grape" in fruits
False
>>> "grape" not in fruits
True
These examples show that you can also check whether a given value is contained in a set by using the membership operators in
and not in
.
Now that you know how the in
and not in
operators work with different built-in data types, it’s time to put these operators into action with a couple of examples.
Putting Python’s in
and not in
Operators Into Action
Membership tests with in
and not in
are pretty common operations in programming. You’ll find these kinds of tests in many existing Python codebases, and you’ll use them in your code as well.
In the following sections, you’ll learn how to replace Boolean expressions based on the or
operator with membership tests. Because membership tests can be quite common in your code, you’ll also learn how to make these tests more efficient.
Replacing Chained or
Operators
Using a membership test to replace a compound Boolean expression with several or
operators is a useful technique that allows you to simplify your code and make it more readable.
To see this technique in action, say that you need to write a function that takes a color name as a string and determines whether it’s a primary color. To figure this out, you’ll use the RGB (red, green, and blue) color model:
>>>
>>> def is_primary_color(color):
... color = color.lower()
... return color == "red" or color == "green" or color == "blue"
...
>>> is_primary_color("yellow")
False
>>> is_primary_color("green")
True
In is_primary_color()
, you use a compound Boolean expression that uses the or
operator to check if the input color is either red, green, or blue. Even though this function works as expected, the condition may be confusing and difficult to read and understand.
The good news is that you can replace the above condition with a compact and readable membership test:
>>>
>>> def is_primary_color(color):
... primary_colors = {"red", "green", "blue"}
... return color.lower() in primary_colors
...
>>> is_primary_color("yellow")
False
>>> is_primary_color("green")
True
Now your function uses the in
operator to check whether the input color is red, green, or blue. Assigning the set of primary colors to a properly named variable like primary_colors
also helps to make your code more readable. The final check is pretty clear now. Anyone reading your code will immediately understand that you’re trying to determine if the input color is a primary color according to the RGB color model.
If you look at the example again, then you’ll notice that the primary colors have been stored in a set. Why? You’ll find your answer in the following section.
Writing Efficient Membership Tests
Python uses a data structure called a hash table to implement dictionaries and sets. Hash tables have a remarkable property: looking for any given value in the data structure takes about the same time, no matter how many values the table has. Using Big O notation, you’ll say that value lookups in hash tables have a time complexity of O(1), which makes them super fast.
Now, what does this feature of hash tables have to do with membership tests on dictionaries and sets? Well, it turns out that the in
and not in
operators work very quickly when they operate on these types. This detail allows you to optimize your code’s performance by favoring dictionaries and sets over lists and other sequences in membership tests.
To have an idea of how much more efficient than a list a set can be, go ahead and create the following script:
# performance.py
from timeit import timeit
a_list = list(range(100_000))
a_set = set(range(100_000))
list_time = timeit("-1 in a_list", number=1, globals=globals())
set_time = timeit("-1 in a_set", number=1, globals=globals())
print(f"Sets are {(list_time / set_time):.2f} times faster than Lists")
This script creates a list of integer numbers with one hundred thousand values and a set with the same number of elements. Then the script computes the time that it takes to determine if the number -1
is in the list and the set. You know up front that -1
doesn’t appear in the list or set. So, the membership operator will have to check all the values before getting a final result.
As you already know, when the in
operator searches for a value in a list, it uses an algorithm with a time complexity of O(n). On the other hand, when the in
operator searches for a value in a set, it uses the hash table lookup algorithm, which has a time complexity of O(1). This fact can make a big difference in terms of performance.
Go ahead and run your script from the command line using the following command:
$ python performance.py
Sets are 1563.33 times faster than Lists
Although your command’s output may be slightly different, it’ll still show a significant performance difference when you use a set instead of a list in this specific membership test. With a list, the processing time will be proportional to the number of values. With a set, the time will be pretty much the same for any number of values.
This performance test shows that when your code is doing membership checks on large collections of values, you should use sets instead of lists whenever possible. You’ll also benefit from sets when your code performs several membership tests during its execution.
However, note that it’s not a good idea to convert an existing list into a set just to perform a few membership tests. Remember that converting a list into a set is an operation with O(n) time complexity.
Using operator.contains()
for Membership Tests
The in
operator has an equivalent function in the operator
module, which comes in the standard library. The function is called contains()
. It takes two arguments—a collection of values and a target value. It returns True
if the input collection contains the target value:
>>>
>>> from operator import contains
>>> contains([2, 3, 5, 9, 7], 5)
True
>>> contains([2, 3, 5, 9, 7], 8)
False
The first argument to contains()
is the collection of values, and the second argument is the target value. Note that the order of arguments differs from a regular membership operation, where the target value comes first.
This function comes in handy when you’re using tools like map()
, or filter()
to process iterables in your code. For example, say you have a bunch of Cartesian points stored as tuples inside a list. You want to create a new list containing only the points that aren’t over the coordinate axis. Using the filter()
function, you can come up with the following solution:
>>>
>>> points = [
... (1, 3),
... (5, 0),
... (3, 7),
... (0, 6),
... (8, 3),
... (2, 0),
... ]
>>> list(filter(lambda point: not contains(point, 0), points))
[(1, 3), (3, 7), (8, 3)]
In this example, you use filter()
to retrieve the points that don’t contain a 0
coordinate. To do this, you use contains()
in a lambda
function. Because filter()
returns an iterator, you wrap up everything in a call to list()
to convert the iterator into a list of points.
Even though the construct in the above example works, it’s quite complex because it implies importing contains()
, creating a lambda
function on top of it, and calling a couple of functions. You can get the same result using a list comprehension either with contains()
or the not in
operator directly:
>>>
>>> [point for point in points if not contains(point, 0)]
[(1, 3), (3, 7), (8, 3)]
>>> [point for point in points if 0 not in point]
[(1, 3), (3, 7), (8, 3)]
The above list comprehensions are shorter and arguably more readable than the equivalent filter()
call from the previous example. They’re also less complex because you don’t need to create a lambda
function or call list()
, so you’re reducing the knowledge requirements.
Supporting Membership Tests in User-Defined Classes
Providing a .__contains__()
method is the most explicit and preferred way to support membership tests in your own classes. Python will automatically call this special method when you use an instance of your class as the right operand in a membership test.
You’ll likely add a .__contains__()
method only to classes that’ll work as collections of values. That way, the users of your class will be able to determine if a given value is stored in a specific instance of your class.
As an example, say that you need to create a minimal stack data structure to store values following the LIFO (last in, first out) principle. One requirement of your custom data structure is to support membership tests. So, you end up writing the following class:
# stack.py
class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def __contains__(self, item):
return item in self.items
Your Stack
class supports the two core functionalities of stack data structures. You can push a value to the top of the stack and pop a value from the top of the stack. Note that your data structure uses a list
object under the hood to store and manipulate the actual data.
Your class also supports membership tests with the in
and not in
operators. To do this, the class implements a .__contains__()
method that relies on the in
operator itself.
To try out your class, go ahead and run the following code:
>>>
>>> from stack import Stack
>>> stack = Stack()
>>> stack.push(1)
>>> stack.push(2)
>>> stack.push(3)
>>> 2 in stack
True
>>> 42 in stack
False
>>> 42 not in stack
True
Your class fully supports the in
and not in
operators. Great job! You now know how to support membership tests in your own classes.
Note that if a given class has a .__contains__()
method, then the class doesn’t have to be iterable for the membership operators to work. In the example above, Stack
isn’t iterable, and the operators still work because they retrieve their result from the .__contains__()
method.
There are at least two more ways to support membership tests in user-defined classes apart from providing a .__contains__()
method. If your class has either an .__iter__()
or a .__getitem__()
method, then the in
and not in
operators also work.
Consider the following alternative version of Stack
:
# stack.py
class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def __iter__(self):
yield from self.items
The .__iter__()
special method makes your class iterable, which is enough for membership tests to work. Go ahead and give it a try!
Another way to support membership tests is to implement a .__getitem__()
method that handles indexing operations using zero-based integer indices in your classes:
# stack.py
class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def __getitem__(self, index):
return self.items[index]
Python automatically calls the .__getitem__()
method when you perform indexing operations on the underlying object. In this example, when you do stack[0]
, you’ll get the first item in the Stack
instance. Python takes advantage of .__getitem__()
to make the membership operators work correctly.
Conclusion
Now you know how to perform membership tests using Python’s in
and not in
operators. This type of test allows you to check if a given value is present in a collection of values, which is a pretty common operation in programming.
In this tutorial, you’ve learned how to:
- Run membership tests using Python’s
in
andnot in
operators - Use the
in
andnot in
operators with different data types - Work with
operator.contains()
, the equivalent function to thein
operator - Support
in
andnot in
in your own classes
With this knowledge, you’re good to go with membership tests using Python’s in
and not in
operators in your code.
Use the in operator to check if the key is not in the dictionary Python. Or you can use the get() method to check if the key exists.
Note: has_keys() method has been removed from the Python3 version. Therefore, it can be used in Python2 only.
Simple example code.
Using the in operator
This operator is used to check if one value is a member of another. It returns a boolean value.
dict_1 = {"a": 1, "b": 2, "c": 3}
if "a" in dict_1:
print("Exists")
else:
print("Does not exist")
Output:
Similarly, the not in
the operator can also be used to check if a key does not exist in a dictionary.
dict_1 = {"a": 1, "b": 2, "c": 3}
if "e" not in dict_1:
print("Key e does not exist")
Output: Key e does not exist
Do comment if you have any doubts and suggestions on this Python dictionary topic.
Note: IDE: PyCharm 2021.3.3 (Community Edition)
Windows 10
Python 3.10.1
All Python Examples are in Python 3, so Maybe its different from python 2 or upgraded versions.
Degree in Computer Science and Engineer: App Developer and has multiple Programming languages experience. Enthusiasm for technology & like learning technical.