Python index of item in list without error?

a = [1]
try:
    index_value = a.index(44)
except ValueError:
    index_value = -1

How about this?


I agree with the general solution that was pointed out, but I'd like to look a bit more into the approaches that were explained in the answers and comments to see which one is more efficient and in which situations.

First of all, the three basic approaches:

>>> def my_index(L, obj):
...     for i, el in enumerate(L):
...             if el == obj:
...                     return i
...     return -1
... 
>>> def my_index2(L, obj):
...     try:
...             return L.index(obj)
...     except ValueError:
...             return -1
... 
>>> def my_index3(L, obj):
...     if obj in L:
...             return L.index(obj)
...     return -1
... 

The first and second solutions scan the list only once, and so you may think that they are faster than the third one because it scans the list twice. So let's see:

>>> timeit.timeit('my_index(L, 24999)', 'from __main__ import my_index, L', number=1000)
1.6892211437225342
>>> timeit.timeit('my_index2(L, 24999)', 'from __main__ import my_index2, L', number=1000)
0.403195858001709
>>> timeit.timeit('my_index3(L, 24999)', 'from __main__ import my_index3, L', number=1000)
0.7741198539733887

Well the second is really the fastest, but you can notice that the first one is much slower than the third one, even though it scans the list only once. If we increase the size of the list things does not change much:

>>> L = list(range(2500000))
>>> timeit.timeit('my_index(L, 2499999)', 'from __main__ import my_index, L', number=100)
17.323430061340332
>>> timeit.timeit('my_index2(L, 2499999)', 'from __main__ import my_index2, L', number=100)
4.213982820510864
>>> timeit.timeit('my_index3(L, 2499999)', 'from __main__ import my_index3, L', number=100)
8.406487941741943

The first one is still 2x times slower.

and if we search something that it's not in the list things get even worse for the first solution:

>>> timeit.timeit('my_index(L, None)', 'from __main__ import my_index, L', number=100)
19.055058002471924
>>> timeit.timeit('my_index2(L, None)', 'from __main__ import my_index2, L', number=100)
5.785136938095093
>>> timeit.timeit('my_index3(L, None)', 'from __main__ import my_index3, L', number=100)
5.46164608001709

As you can see in this case the third solution beats even the second one, and both are almost 4x faster than the python code. Depending on how often you expect the search to fail you want to choose #2 or #3(even though in 99% of the cases number #2 is better).

As a general rule, if you want to optimize something for CPython then you want to do as much iterations "at C level" as you can. In your example iterating using a for loop is exactly something you do not want to do.


It's not a good idea to return -1 as that is a valid index in Python (see Python list.index throws exception when index not found).

Probably best to catch the index error and act accordingly.

Tags:

Python