To get the index of an element in a Python list, call .index() on the list:
fruits = ["apple", "pear", "plum"]
fruits.index("pear") # 1
Two things to know before you rely on it. It returns only the first match, and a value that is not there raises ValueError rather than returning -1.
Both have straightforward fixes, covered below along with finding every occurrence and what to do with NumPy arrays. Output is from Python 3.12.5.

.index() stops at the first match and never mentions the second.Using list.index() to find an element
The method takes the value you are looking for and gives back its position:
fruits = ["apple", "pear", "plum", "pear"]
print("fruits.index('pear') ->", fruits.index("pear"))
print("fruits.index('plum') ->", fruits.index("plum"))
print()
print("positions count from 0, so 'apple' is 0 and 'pear' is 1.")
print("Note that 'pear' appears twice and only the FIRST is reported.")
Output:
fruits.index('pear') -> 1
fruits.index('plum') -> 2
positions count from 0, so 'apple' is 0 and 'pear' is 1.
Note that 'pear' appears twice and only the FIRST is reported.

.index() returns the position of the first match.Positions start at 0, so the first item is at index 0. That is the same numbering used by slicing and by range(len(items)).
Why index() raises ValueError instead of returning -1
A missing value is treated as a mistake, not as a result:
fruits = ["apple", "pear", "plum"]
try:
fruits.index("kiwi")
except ValueError as err:
print("fruits.index('kiwi') -> ValueError:", err)
print()
print("an empty list behaves the same way:")
try:
[].index(1)
except ValueError as err:
print("[].index(1) -> ValueError:", err)
print()
print("so a missing value is an exception, not a -1 or a None.")
Output:
fruits.index('kiwi') -> ValueError: 'kiwi' is not in list
an empty list behaves the same way:
[].index(1) -> ValueError: 1 is not in list
so a missing value is an exception, not a -1 or a None.

ValueError: 'kiwi' is not in list.This is deliberate. Returning -1 would be a valid index in Python, since negative indexes count from the end, so items[-1] would silently hand back the last item instead of signalling a miss.
Python has no indexOf method
If you arrived from JavaScript, the method you want is spelled differently and behaves differently:
print("Python lists do not have the methods you may be looking for:")
print(" hasattr(list, 'indexOf') ->", hasattr(list, "indexOf"))
print(" hasattr(list, 'find') ->", hasattr(list, "find"))
print(" hasattr(list, 'index') ->", hasattr(list, "index"))
print()
print("STRINGS do have find, and it returns -1 instead of raising:")
print(" 'hello'.find('z') ->", "hello".find("z"))
print(" 'hello'.find('ll') ->", "hello".find("ll"))
print()
print(" 'hello'.index('z') ->", end=" ")
try:
"hello".index("z")
except ValueError as err:
print("ValueError:", err)
print()
print("So: lists get .index() only. Strings get both .find() and .index().")
Output:
Python lists do not have the methods you may be looking for:
hasattr(list, 'indexOf') -> False
hasattr(list, 'find') -> False
hasattr(list, 'index') -> True
STRINGS do have find, and it returns -1 instead of raising:
'hello'.find('z') -> -1
'hello'.find('ll') -> 2
'hello'.index('z') -> ValueError: substring not found
So: lists get .index() only. Strings get both .find() and .index().

.index() only; strings also have .find().| You want | JavaScript | Python |
|---|---|---|
| Position, or a miss signal | arr.indexOf(x) returns -1 | list.index(x) raises ValueError |
| Position in a string | str.indexOf(x) returns -1 | str.find(x) returns -1 |
| Is it there at all? | arr.includes(x) | x in list |
| Every position | loop or flatMap | [i for i, v in enumerate(list) if v == x] |
str.find() is the closest match to indexOf and it does return -1. Lists simply never got an equivalent.
Getting the index without an exception
Three ways to turn a miss into a value you can test:
fruits = ["apple", "pear", "plum"]
def index_or(items, value, default=-1):
"""The equivalent of JavaScript's indexOf."""
return next((i for i, item in enumerate(items) if item == value), default)
print("index_or(fruits, 'pear') ->", index_or(fruits, "pear"))
print("index_or(fruits, 'kiwi') ->", index_or(fruits, "kiwi"), " no exception")
print("index_or(fruits, 'kiwi', None) ->", index_or(fruits, "kiwi", None))
print()
print("or handle the exception where it happens:")
try:
position = fruits.index("kiwi")
except ValueError:
position = -1
print(" position ->", position)
print()
print("checking first also works, but scans the list twice:")
position = fruits.index("pear") if "pear" in fruits else -1
print(" position ->", position)
Output:
index_or(fruits, 'pear') -> 1
index_or(fruits, 'kiwi') -> -1 no exception
index_or(fruits, 'kiwi', None) -> None
or handle the exception where it happens:
position -> -1
checking first also works, but scans the list twice:
position -> 1
The next(...) form is the closest thing to indexOf, and the default is yours to choose. Use None rather than -1 if there is any chance the result gets used as an index later.
Wrapping the call in try is equally valid and reads well when a miss is genuinely unexpected. Checking with in first works too, but walks the list twice. Checking whether a list contains a value covers that test on its own.
Find all indexes of an element in a list
.index() stops at the first match, so repeats need a comprehension:
fruits = ["apple", "pear", "plum", "pear", "pear"]
print("the list ->", fruits)
print("fruits.index('pear') ->", fruits.index("pear"), " first only")
print()
positions = [i for i, fruit in enumerate(fruits) if fruit == "pear"]
print("every position ->", positions)
print("how many ->", len(positions))
print("last one ->", positions[-1])
print()
print("the same idea with a condition instead of a value:")
numbers = [4, 9, 2, 9, 1]
print(" numbers ->", numbers)
print(" positions over 3 ->", [i for i, n in enumerate(numbers) if n > 3])
Output:
the list -> ['apple', 'pear', 'plum', 'pear', 'pear']
fruits.index('pear') -> 1 first only
every position -> [1, 3, 4]
how many -> 3
last one -> 4
the same idea with a condition instead of a value:
numbers -> [4, 9, 2, 9, 1]
positions over 3 -> [0, 1, 3]

enumerate() returns every matching position.The same pattern takes a condition instead of a value, which is how you find every position above a threshold or every string starting with a letter.
It returns an empty list when nothing matches, so there is no exception to handle. Looping with an index explains the enumerate() part.
Searching from a given position
.index() accepts start and end arguments:
fruits = ["apple", "pear", "plum", "pear"]
print("searching from a position onwards:")
print(" fruits.index('pear') ->", fruits.index("pear"))
print(" fruits.index('pear', 2) ->", fruits.index("pear", 2), " skips the first one")
print()
print("with an end position too, which is exclusive:")
print(" fruits.index('pear', 0, 2) ->", fruits.index("pear", 0, 2))
try:
fruits.index("pear", 2, 3)
except ValueError as err:
print(" fruits.index('pear', 2, 3) -> ValueError:", err)
print()
print("this is how you walk through repeats one at a time,")
print("though the comprehension above is usually clearer.")
Output:
searching from a position onwards:
fruits.index('pear') -> 1
fruits.index('pear', 2) -> 3 skips the first one
with an end position too, which is exclusive:
fruits.index('pear', 0, 2) -> 1
fruits.index('pear', 2, 3) -> ValueError: 'pear' is not in list
this is how you walk through repeats one at a time,
though the comprehension above is usually clearer.
The end position is exclusive, matching slicing. Calling it repeatedly with a moving start is the classic way to step through repeats, though the comprehension above is easier to read.
index() compares with equality, not identity
This produces results that look wrong until you see why:
mixed = [1.0, 1, True]
print("the list ->", mixed)
print("mixed.index(1) ->", mixed.index(1))
print()
print("1.0 == 1 is", 1.0 == 1, "so the float matched first.")
print(".index() compares with == , not with 'is the same object'.")
print()
print("True is even stranger:")
print(" True == 1 ->", True == 1)
print(" [True].index(1) ->", [True].index(1))
print()
print("case matters for strings:")
names = ["Ann", "bob"]
try:
nam