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
