Get the Index of an Element in a Python List (index, all matches)

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.

Diagram of a Python list showing that the index method returns only the first matching position
.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.
Command Prompt showing the Python list index method returning the position of an element
.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.
Command Prompt showing the Python ValueError raised when a value is not in the list
A missing value raises 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().
Command Prompt showing that Python lists have no indexOf or find method while strings have find
Lists have .index() only; strings also have .find().
You wantJavaScriptPython
Position, or a miss signalarr.indexOf(x) returns -1list.index(x) raises ValueError
Position in a stringstr.indexOf(x) returns -1str.find(x) returns -1
Is it there at all?arr.includes(x)x in list
Every positionloop 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]
Command Prompt showing how to find all indexes of a repeated element in a Python list using enumerate
A comprehension over 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:
    names.index("ann")
except ValueError as err:
    print("  names.index('ann') -> ValueError:", err)
print("  case insensitive ->",
      next((i for i, n in enumerate(names) if n.lower() == "ann"), None))

Output:

the list -> [1.0, 1, True]
mixed.index(1) -> 0

1.0 == 1 is True so the float matched first.
.index() compares with == , not with 'is the same object'.

True is even stranger:
  True == 1   -> True
  [True].index(1) -> 0

case matters for strings:
  names.index('ann') -> ValueError: 'ann' is not in list
  case insensitive -> 0
Command Prompt showing that the Python list index method matches 1.0 for 1 and is case sensitive for strings
.index() uses ==, so 1.0 matches 1.

1.0 == 1 is true in Python, and True == 1 is as well, so a list holding a mix of those types can match somewhere you did not expect.

String comparison is case sensitive. To ignore case, filter with enumerate and compare lowercased values.

Index of the largest or smallest value

Combine .index() with max() or min():

scores = [4, 9, 2, 9, 1]

print("scores ->", scores)
print()
print("position of the highest value:")
print("  scores.index(max(scores)) ->", scores.index(max(scores)))
print("position of the lowest:")
print("  scores.index(min(scores)) ->", scores.index(min(scores)))
print()

print("the LAST highest, when there are ties:")
last = len(scores) - 1 - scores[::-1].index(max(scores))
print("  ->", last)
print()

print("and every position that ties for the highest:")
best = max(scores)
print("  ->", [i for i, s in enumerate(scores) if s == best])

Output:

scores -> [4, 9, 2, 9, 1]

position of the highest value:
  scores.index(max(scores)) -> 1
position of the lowest:
  scores.index(min(scores)) -> 4

the LAST highest, when there are ties:
  -> 3

and every position that ties for the highest:
  -> [1, 3]

Ties go to the first occurrence, as always. Reversing the list and adjusting the arithmetic gives the last one instead, and a comprehension gives every position that ties.

Finding an item in a list of dictionaries

.index() needs the whole value, which rarely helps for records:

people = [{"name": "ann", "age": 30},
          {"name": "bob", "age": 25}]

print("lists of dictionaries need a condition, not a value:")
position = next((i for i, p in enumerate(people) if p["name"] == "bob"), None)
print("  first person named bob ->", position)
print()

print("lists of tuples can use .index() directly, if you have the whole tuple:")
pairs = [("a", 1), ("b", 2)]
print("  pairs.index(('b', 2)) ->", pairs.index(("b", 2)))
print()

print("nested lists work the same way:")
grid = [[1, 2], [3, 4]]
print("  grid.index([3, 4]) ->", grid.index([3, 4]))

Output:

lists of dictionaries need a condition, not a value:
  first person named bob -> 1

lists of tuples can use .index() directly, if you have the whole tuple:
  pairs.index(('b', 2)) -> 1

nested lists work the same way:
  grid.index([3, 4]) -> 1

Searching by one field means a condition, so next(...) over enumerate is the tool again. Tuples and nested lists can use .index() directly, because you can write the value out in full.

Getting the index in a NumPy array

NumPy arrays have no .index() at all:

import numpy as np

arr = np.array([4, 9, 2, 9, 1])
print("array ->", arr)
print()
print("NumPy arrays have no .index() method. Use np.where:")
print("  np.where(arr == 9)[0] ->", np.where(arr == 9)[0])
print("  as a plain list       ->", np.where(arr == 9)[0].tolist())
print()
print("the first match only:")
print("  np.where(arr == 9)[0][0] ->", np.where(arr == 9)[0][0])
print()
print("and the positions of the largest and smallest:")
print("  arr.argmax() ->", arr.argmax())
print("  arr.argmin() ->", arr.argmin())
print()
print("np.where returns every match in one pass, which beats looping.")

Output:

array -> [4 9 2 9 1]

NumPy arrays have no .index() method. Use np.where:
  np.where(arr == 9)[0] -> [1 3]
  as a plain list       -> [1, 3]

the first match only:
  np.where(arr == 9)[0][0] -> 1

and the positions of the largest and smallest:
  arr.argmax() -> 1
  arr.argmin() -> 4

np.where returns every match in one pass, which beats looping.

np.where() returns every matching position in one pass, so there is no first-match limitation to work around. Take [0][0] if you only want the first.

argmax() and argmin() give the positions of the extremes directly. Using np.where goes into the conditional forms.

Which method should you use?

SituationUse
A value you know is presentitems.index(value)
It might be missingnext((i for i, v in enumerate(items) if v == value), -1)
Every occurrence[i for i, v in enumerate(items) if v == value]
A condition rather than a valuenext((i for i, v in enumerate(items) if v > 10), None)
The largest or smallestitems.index(max(items))
A NumPy arraynp.where(arr == value)[0]
A substring in a stringtext.find(sub), which returns -1

More list guides worth reading:

Frequently asked questions

How do I get the index of an element in a Python list?

Call my_list.index(value). It returns the position of the first match, counting from 0, and raises ValueError if the value is not present.

Does Python have an indexOf method?

No. Lists have .index(), which raises instead of returning -1. Strings have .find(), which does return -1 and is the closest equivalent.

How do I find the index without getting a ValueError?

Use next((i for i, v in enumerate(items) if v == value), -1), or wrap .index() in a try block. Both let you choose what a miss looks like.

How do I find all indexes of an element in a list?

[i for i, v in enumerate(items) if v == value]. It returns every matching position, and an empty list when there are none.

Why does index() only return the first match?

Because it stops as soon as it finds one. Use the comprehension above when a value repeats and you need every position.

How do I get the index of the maximum value?

items.index(max(items)) for a list, or arr.argmax() for a NumPy array. Both report the first position when several values tie.

Why did index() match 1 when my list holds 1.0?

It compares with ==, and 1.0 == 1 is true in Python. The behaviour is set out in the Python list methods documentation.