Checking whether a Python array contains a value is done with the in operator:
fruits = ["apple", "pear", "plum"]
"pear" in fruits # True
"kiwi" not in fruits # True
Note the vocabulary. What most people call an array, Python calls a list, and in works on both that and a NumPy array. There is no includes() or contains() method.
Below: conditions rather than exact values, checking several at once, NumPy arrays, and why converting to a set can be worth it. Output is from Python 3.12.5, NumPy 2.5.3.
Check if a list or array contains a value
in returns True or False and reads like English:
fruits = ["apple", "pear", "plum"]
print('"pear" in fruits ->', "pear" in fruits)
print('"kiwi" in fruits ->', "kiwi" in fruits)
print('"kiwi" not in fruits ->', "kiwi" not in fruits)
print()
if "pear" in fruits:
print("found it")
print()
print("`in` is an operator, not a method, so there are no brackets")
print("and nothing to import.")
Output:
"pear" in fruits -> True
"kiwi" in fruits -> False
"kiwi" not in fruits -> True
found it
`in` is an operator, not a method, so there are no brackets
and nothing to import.
in and not in answer the question directly.It is an operator rather than a method, so there are no brackets and nothing to import. not in is a single operator too, which reads better than not (x in y).
Python has no includes() or contains() method
If you are coming from another language, the method you want does not exist:
fruits = ["apple", "pear"]
print("Python lists have no includes() or contains() method:")
print(" hasattr(list, 'includes') ->", hasattr(list, "includes"))
print(" hasattr(list, 'contains') ->", hasattr(list, "contains"))
print()
try:
fruits.includes("pear")
except AttributeError as err:
print(" fruits.includes('pear') -> AttributeError:", err)
print()
print("the operator replaces all of them:")
print(" JavaScript arr.includes(x) -> Python x in arr")
print(" Java list.contains(x) -> Python x in list")
Output:
Python lists have no includes() or contains() method:
hasattr(list, 'includes') -> False
hasattr(list, 'contains') -> False
fruits.includes('pear') -> AttributeError: 'list' object has no attribute 'includes'
the operator replaces all of them:
JavaScript arr.includes(x) -> Python x in arr
Java list.contains(x) -> Python x in list
| Language | Method | Python |
|---|---|---|
| JavaScript | arr.includes(x) | x in arr |
| Java | list.contains(x) | x in list |
| C# | list.Contains(x) | x in list |
| PHP | in_array(x, arr) | x in arr |
One operator covers every container, which is why Python never added the methods.
The in operator on lists, strings, sets and dictionaries
The same syntax, with one behaviour worth memorising:
print("the same operator works on every container:")
print(' "pear" in ["apple", "pear"] ->', "pear" in ["apple", "pear"])
print(' "pear" in ("apple", "pear") ->', "pear" in ("apple", "pear"))
print(' "pear" in {"apple", "pear"} ->', "pear" in {"apple", "pear"})
print(' "ea" in "pear" ->', "ea" in "pear", " substring")
print()
scores = {"ann": 90, "bob": 85}
print("on a dictionary it checks the KEYS, not the values:")
print(' "ann" in scores ->', "ann" in scores)
print(" 90 in scores ->", 90 in scores, " <- a value, so False")
print(" 90 in scores.values() ->", 90 in scores.values())
print()
print("that surprises people, so say .values() when you mean values.")
Output:
the same operator works on every container:
"pear" in ["apple", "pear"] -> True
"pear" in ("apple", "pear") -> True
"pear" in {"apple", "pear"} -> True
"ea" in "pear" -> True substring
on a dictionary it checks the KEYS, not the values:
"ann" in scores -> True
90 in scores -> False <- a value, so False
90 in scores.values() -> True
that surprises people, so say .values() when you mean values.
in checks the keys.90 in scores is False even when 90 is one of the values, because a dictionary searches its keys. Add .values() when you mean the other half.
On a string, in tests for a substring rather than a single character, which is the usual way to check text. Iterating a dictionary covers the keys and values distinction in more depth.
Checking for a condition rather than an exact value
any() and all() extend the idea:
fruits = ["apple", "pear", "plum"]
print("an exact value:")
print(' "pear" in fruits ->', "pear" in fruits)
print()
print("anything matching a condition:")
print(" any(f.startswith('p') for f in fruits) ->",
any(f.startswith("p") for f in fruits))
print(" which ones ->", [f for f in fruits if f.startswith("p")])
print()
print("every item matching:")
print(" all(len(f) > 3 for f in fruits) ->", all(len(f) > 3 for f in fruits))
print()
print("case insensitive:")
print(' "PEAR" in fruits ->', "PEAR" in fruits)
print(' lowercased ->', "pear" in [f.lower() for f in fruits])
print()
print("`in` compares exactly, so casing and whitespace both count.")
Output:
an exact value:
"pear" in fruits -> True
anything matching a condition:
any(f.startswith('p') for f in fruits) -> True
which ones -> ['pear', 'plum']
every item matching:
all(len(f) > 3 for f in fruits) -> True
case insensitive:
"PEAR" in fruits -> False
lowercased -> True
`in` compares exactly, so casing and whitespace both count.
any(...) stops at the first match, so it costs nothing extra on a large list. Swap it for a comprehension when you want the matching items rather than a yes or no.
Comparison is exact, so "PEAR" does not match "pear". Lowercase both sides when case should not matter.
Checking for several values at once
Combine in with any, all or set operations:
fruits = ["apple", "pear", "plum"]
wanted = ["pear", "kiwi"]
print("are they ALL there?")
print(" all(w in fruits for w in wanted) ->", all(w in fruits for w in wanted))
print()
print("is ANY of them there?")
print(" any(w in fruits for w in wanted) ->", any(w in fruits for w in wanted))
print()
print("which ones are missing?")
print(" ->", [w for w in wanted if w not in fruits])
print()
print("with sets, which is faster and reads well:")
print(" set(wanted) & set(fruits) ->", set(wanted) & set(fruits))
print(" set(wanted) - set(fruits) ->", set(wanted) - set(fruits), " the missing ones")
Output:
are they ALL there?
all(w in fruits for w in wanted) -> False
is ANY of them there?
any(w in fruits for w in wanted) -> True
which ones are missing?
-> ['kiwi']
with sets, which is faster and reads well:
set(wanted) & set(fruits) -> {'pear'}
set(wanted) - set(fruits) -> {'kiwi'} the missing ones
Set operations read best when both sides are collections. & gives what they share and - gives what is missing, in one expression each.
Why converting to a set can be much faster
This matters as soon as you check repeatedly:
import timeit
setup = "data = list(range(1_000_000)); lookup = set(data); target = 999_999"
list_time = timeit.timeit("target in data", setup, number=100)
set_time = timeit.timeit("target in lookup", setup, number=100)
print("100 lookups in a collection of 1,000,000 items:")
print(f" list {list_time:.4f}s")
print(f" set {set_time:.6f}s")
print(f" the set is roughly {list_time / set_time:,.0f} times quicker")
print()
print("a list is checked item by item, so a miss reads all million.")
print("a set jumps straight to where the value would be.")
print()
print("converting once pays for itself as soon as you check repeatedly:")
print(" lookup = set(data) # once")
print(" target in lookup # many times")
Output:
100 lookups in a collection of 1,000,000 items:
list 0.7076s
set 0.000007s
the set is roughly 101,081 times quicker
a list is checked item by item, so a miss reads all million.
a set jumps straight to where the value would be.
converting once pays for itself as soon as you check repeatedly:
lookup = set(data) # once
target in lookup # many times
A list is searched item by item, so a value that is absent means reading every one. A set jumps straight to where the value would be, and the gap on a million items runs to many thousands of times.
Building the set costs one pass, so it pays for itself the moment you check more than a handful of times. Keep the list when order matters and build a set alongside it for the lookups.
A set also drops duplicates, so use it for membership rather than as a replacement for the list. Splitting a list is useful when the data is too large to hold at once.
Check if a NumPy array contains a value
in works, but NumPy gives you more:
import numpy as np
arr = np.array([1, 2, 3, 4])
print("the operator works on a NumPy array too:")
print(" 3 in arr ->", 3 in arr)
print()
print("but the array-aware versions do more:")
print(" np.any(arr == 3) ->", np.any(arr == 3))
print(" np.isin(arr, [2, 5]) ->", np.isin(arr, [2, 5]), " one answer per element")
print(" np.isin(arr, [2, 5]).any() ->", np.isin(arr, [2, 5]).any())
print()
print("where it is, not just whether:")
print(" np.where(arr == 3)[0] ->", np.where(arr == 3)[0])
print()
two_d = np.array([[1, 2], [3, 4]])
print("on a 2D array, `in` searches every element:")
print(" 3 in two_d ->", 3 in two_d)
print("to look for a whole ROW:")
print(" [1, 2] in two_d.tolist() ->", [1, 2] in two_d.tolist())
Output:
the operator works on a NumPy array too:
3 in arr -> True
but the array-aware versions do more:
np.any(arr == 3) -> True
np.isin(arr, [2, 5]) -> [False True False False] one answer per element
np.isin(arr, [2, 5]).any() -> True
where it is, not just whether:
np.where(arr == 3)[0] -> [2]
on a 2D array, `in` searches every element:
3 in two_d -> True
to look for a whole ROW:
[1, 2] in two_d.tolist() -> True
np.isin() answers per element, which is what you want for filtering. Adding .any() collapses that back to a single True or False.
On a 2D array, in looks at every element regardless of rows. To search for a whole row, compare against .tolist(). np.where gives the positions rather than a yes or no.
ValueError: the truth value of an array is ambiguous
The error that follows people around NumPy:
import numpy as np
arr = np.array([1, 2, 3])
print("this is the error everyone meets with NumPy:")
try:
if arr == 2:
print("never reached")
except ValueError as err:
print(" if arr == 2 -> ValueError:", err)
print()
print("arr == 2 is not True or False, it is an array:")
print(" arr == 2 ->", arr == 2)
print()
print("so say which you mean:")
print(" (arr == 2).any() ->", (arr == 2).any(), " is it anywhere?")
print(" (arr == 2).all() ->", (arr == 2).all(), " is it everywhere?")
Output:
this is the error everyone meets with NumPy:
if arr == 2 -> ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
arr == 2 is not True or False, it is an array:
arr == 2 -> [False True False]
so say which you mean:
(arr == 2).any() -> True is it anywhere?
(arr == 2).all() -> False is it everywhere?
arr == 2 is an array of booleans, not a single True or False.arr == 2 compares every element and returns an array of results. An if needs one answer, and NumPy refuses to guess whether you meant any of them or all of them.
.any() and .all() say which, and the error message names them both.
Gotchas worth knowing
Four results that look wrong until explained:
print("floats do not always compare the way they look:")
print(" 0.1 + 0.2 in [0.3] ->", 0.1 + 0.2 in [0.3])
print(" because 0.1 + 0.2 is", repr(0.1 + 0.2))
print(" use a tolerance instead:")
print(" any(abs(0.1 + 0.2 - x) < 1e-9 for x in [0.3]) ->",
any(abs(0.1 + 0.2 - x) < 1e-9 for x in [0.3]))
print()
print("an empty string is inside every string:")
print(' "" in "abc" ->', "" in "abc")
print()
print("nested lists compare as whole items:")
print(" [1, 2] in [[1, 2], [3, 4]] ->", [1, 2] in [[1, 2], [3, 4]])
print(" 1 in [[1, 2], [3, 4]] ->", 1 in [[1, 2], [3, 4]], " <- not found at the top level")
print()
print("True and 1 are equal, so they match each other:")
print(" True in [1, 2] ->", True in [1, 2])
Output:
floats do not always compare the way they look:
0.1 + 0.2 in [0.3] -> False
because 0.1 + 0.2 is 0.30000000000000004
use a tolerance instead:
any(abs(0.1 + 0.2 - x) < 1e-9 for x in [0.3]) -> True
an empty string is inside every string:
"" in "abc" -> True
nested lists compare as whole items:
[1, 2] in [[1, 2], [3, 4]] -> True
1 in [[1, 2], [3, 4]] -> False <- not found at the top level
True and 1 are equal, so they match each other:
True in [1, 2] -> True
Floating point is the sharpest one. 0.1 + 0.2 is not exactly 0.3, so an exact membership test fails. Compare with a tolerance instead.
The empty string is contained in every string, nested lists match as whole items rather than by their contents, and True matches 1 because they are equal in Python.
Quick reference for contains checks
| What you have | Use |
|---|---|
| A list or tuple | value in items |
| A string | substring in text |
| A dictionary, by key | key in d |
| A dictionary, by value | value in d.values() |
| Repeated lookups | value in set(items) |
| A condition, not a value | any(f(x) for x in items) |
| A NumPy array | np.any(arr == value) or np.isin() |
| Several values | set(wanted) & set(items) |
Guides that pair well with this one:
- Get the index of an element in a list
- Loop over a list with its index
- Check if a key exists in a dictionary
- Iterate through a dictionary
- NumPy where
- Split a list in Python
Frequently asked questions
How do I check if an array contains a value in Python?
Use the in operator: value in my_list. It returns True or False and works on lists, tuples, sets, strings and NumPy arrays.
Does Python have an includes() or contains() method?
No. The in operator replaces both. arr.includes(x) in JavaScript is x in arr in Python.
Why does ‘in’ return False for a dictionary value?
Because in searches a dictionary’s keys. Use value in d.values() to check the values instead.
Is a set faster than a list for checking membership?
Much faster on anything large. A list is searched item by item; a set goes straight to the value. Converting costs one pass, so it pays off as soon as you check repeatedly.
How do I check if a NumPy array contains a value?
value in arr works, or np.any(arr == value). Use np.isin(arr, values) to test many values at once.
Why do I get ‘truth value of an array is ambiguous’?
Because arr == value returns an array of booleans, and if needs one answer. Use (arr == value).any() or .all().
How do I check if a list contains a value ignoring case?
Compare lowercased values: "pear" in [f.lower() for f in fruits]. The operator itself is described in the Python membership test documentation.
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn