a = [1]
try:
index_value = a.index(44)
except ValueError:
index_value = -1
How about this?
Answer from Jakob Bowyer on Stack Overflowa = [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.
If you don't care where the matching element is, then use:
found = x in somelist
If you do care, then use a LBYL style with a conditional expression:
i = somelist.index(x) if x in somelist else None
implement your own index for list?
class mylist(list):
def index_withoutexception(self,i):
try:
return self.index(i)
except:
return -1
So, you can use list, and with your index2, return what you want in case of error.
You can use it like this:
l = mylist([1,2,3,4,5]) # This is the only difference with a real list
l.append(4) # l is a list.
l.index_withoutexception(19) # return -1 or what you want
How to find the index of something in a list without knowing the full item
python - How can I find the index for a given item in a list? - Stack Overflow
Why no .get(idx[, default]) on python list??
Best way to handle list.index(might-not-exist) in python? - Stack Overflow
Hey everyone, I was wondering how I could find the index of an element without knowing the full element. For example, if I have the list of: [‘dog’, ‘cat’, ‘hamster’] and I don’t know what each of them are, but I know that one includes ‘ham’, how could I find the index? Thanks for any help.
>>> ["foo", "bar", "baz"].index("bar")
1
See the documentation for the built-in .index() method of the list:
list.index(x[, start[, end]])Return zero-based index in the list of the first item whose value is equal to x. Raises a
ValueErrorif there is no such item.The optional arguments start and end are interpreted as in the slice notation and are used to limit the search to a particular subsequence of the list. The returned index is computed relative to the beginning of the full sequence rather than the start argument.
Caveats
Linear time-complexity in list length
An index call checks every element of the list in order, until it finds a match. If the list is long, and if there is no guarantee that the value will be near the beginning, this can slow down the code.
This problem can only be completely avoided by using a different data structure. However, if the element is known to be within a certain part of the list, the start and end parameters can be used to narrow the search.
For example:
>>> import timeit
>>> timeit.timeit('l.index(999_999)', setup='l = list(range(0, 1_000_000))', number=1000)
9.356267921015387
>>> timeit.timeit('l.index(999_999, 999_990, 1_000_000)', setup='l = list(range(0, 1_000_000))', number=1000)
0.0004404920036904514
The second call is orders of magnitude faster, because it only has to search through 10 elements, rather than all 1 million.
Only the index of the first match is returned
A call to index searches through the list in order until it finds a match, and stops there. If there could be more than one occurrence of the value, and all indices are needed, index cannot solve the problem:
>>> [1, 1].index(1) # the `1` index is not found.
0
Instead, use a list comprehension or generator expression to do the search, with enumerate to get indices:
>>> # A list comprehension gives a list of indices directly:
>>> [i for i, e in enumerate([1, 2, 1]) if e == 1]
[0, 2]
>>> # A generator comprehension gives us an iterable object...
>>> g = (i for i, e in enumerate([1, 2, 1]) if e == 1)
>>> # which can be used in a `for` loop, or manually iterated with `next`:
>>> next(g)
0
>>> next(g)
2
The list comprehension and generator expression techniques still work if there is only one match, and are more generalizable.
Raises an exception if there is no match
As noted in the documentation above, using .index will raise an exception if the searched-for value is not in the list:
>>> [1, 1].index(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: 2 is not in list
If this is a concern, either explicitly check first using item in my_list, or handle the exception with try/except as appropriate.
The explicit check is simple and readable, but it must iterate the list a second time. See What is the EAFP principle in Python? for more guidance on this choice.
The majority of answers explain how to find a single index, but their methods do not return multiple indexes if the item is in the list multiple times. Use enumerate():
for i, j in enumerate(['foo', 'bar', 'baz']):
if j == 'bar':
print(i)
The index() function only returns the first occurrence, while enumerate() returns all occurrences.
As a list comprehension:
[i for i, j in enumerate(['foo', 'bar', 'baz']) if j == 'bar']
Here's also another small solution with itertools.count() (which is pretty much the same approach as enumerate):
from itertools import izip as zip, count # izip for maximum efficiency
[i for i, j in zip(count(), ['foo', 'bar', 'baz']) if j == 'bar']
This is more efficient for larger lists than using enumerate():
$ python -m timeit -s "from itertools import izip as zip, count" "[i for i, j in zip(count(), ['foo', 'bar', 'baz']*500) if j == 'bar']"
10000 loops, best of 3: 174 usec per loop
$ python -m timeit "[i for i, j in enumerate(['foo', 'bar', 'baz']*500) if j == 'bar']"
10000 loops, best of 3: 196 usec per loop
Hi all,
today it happened once again that I could really use a .get(idx[, default]) method on python lists. Here is a brief example why it could be useful (I know there are many alternative solutions to this specific problem here, so please focus generally on the idea of .get for lists).
file_name = 'test.png'
if '.' in file_name:
extension = file_name.rsplit('.', maxsplit=1)[1]
else:
extension = ''If we had such a method we could make the code much more concise
file_name = 'test.png'
extension = file_name.rsplit('.', maxsplit=1).get(1, '')I wonder why this useful method does not exist, especially since it is available for dicts.
dd = {'a': 'AAA'}
print(f"{dd['a']}; {dd.get('a')}; {dd.get('c')}; {dd.get('c', 'nothing here')}; ")
# AAA; AAA; None; nothing here;Thoughts / ideas why this is not present? Are there valid reasons not to have this method? Is it not available because someone has to invest the work to code it? How could something like this be initiated? :)
thing_index = thing_list.index(elem) if elem in thing_list else -1
One line. Simple. No exceptions.
There is nothing "dirty" about using try-except clause. This is the pythonic way. ValueError will be raised by the .index method only, because it's the only code you have there!
To answer the comment:
In Python, easier to ask forgiveness than to get permission philosophy is well established, and no index will not raise this type of error for any other issues. Not that I can think of any.