Check if an Array Contains a Value in Python (in operator)

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.

Diagram showing the Python in operator used on a list a string a dictionary and a NumPy array
One operator, four containers, and one case where it raises instead.

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.
Command Prompt showing the Python in operator checking whether a list contains a value
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
Command Prompt showing that a Python list has no includes method and raises AttributeError
Python replaces those methods with a single operator.
LanguageMethodPython
JavaScriptarr.includes(x)x in arr
Javalist.contains(x)x in list
C#list.Contains(x)x in list
PHPin_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.
Command Prompt showing the Python in operator on a list tuple set string and dictionary
On a dictionary, 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