Python's list.index(x) throws an exception if the item doesn't exist. Is there a better way to do this that doesn't require handling exceptions?
11 Answers
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
5 Comments
index()? After all you have to look twice: once for the existence and once for the index. This is why C++ containers don't have a exists(), only find().i = next((i for i, t in enumerate(somelist) if x == t), 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
2 Comments
type(l) == list is False here.TL;DR: Exceptions are your friend, and the best approach for the question as stated.
It's easier to ask for forgiveness than permission (EAFP)
The OP clarified in a comment that for their use case, it wasn't actually important to know what the index was. As the accepted answer notes, using x in somelist is the best answer if you don't care.
But I'll assume, as the original question suggests, that you do care what the index is. In that case, I'll note that all the other solutions require scanning the list twice, which can bring a large performance penalty.
Furthermore, as the venerable Raymond Hettinger wrote in a comment
Even if we had list.find that returned a -1 you would still need to test to see if the i == -1 and take some action.
So I'll push back on the assumption in the original question that exceptions should be avoided. I suggest that exceptions are your friend. They're nothing to be scared of, they aren't inefficient, and in fact you need to be conversant with them to write good code.
So I think the best answer is to simply use a try-except approach:
try:
i = somelist.index(x)
except ValueError:
# deal with it
"deal with it" just means do what you need to do: set i to a sentinel value, raise an exception of your own, follow a different code branch, etc.
This is an example of why the Python principle Easier to ask for forgiveness than permission (EAFP) makes sense, in contrast to the if-then-else style of Look before you leap (LBYL)
7 Comments
Write a function that does what you need:
def find_in_iterable(x, iterable):
for i, item in enumerate(iterable):
if item == x:
return i
return None
If you only need to know whether the item exists, but not the index, you can use in:
x in yourlist
3 Comments
'abc'.find('x') == -1, but None would work too. I'll update my answer.Yes, there is. You can eg. do something similar to this:
test = lambda l, e: l.index(e) if e in l else None
which works like that:
>>> a = ['a', 'b', 'c', 'g', 'c']
>>> test(a, 'b')
1
>>> test(a, 'c')
2
>>> test(a, 't')
None
So, basically, test() will return index of the element (second parameter) within given list (first parameter), unless it has not been found (in this case it will return None, but it can be anything you find suitable).
Comments
If you don't care where it is in the sequence, only its presence, then use the in operator. Otherwise, write a function that refactors out the exception handling.
def inlist(needle, haystack):
try:
return haystack.index(needle)
except ...:
return -1
4 Comments
in and eventually returns the index, but I see you are aware of that method and decided to use handling exceptions that may be thrown. My question is: why did you chose catching exception instead of first checking haystack for existence of needle? Is there any reason for that?... in the except ...: line.I like to use Web2py's List class, found in the storage module of its gluon package. The storage module offers list-like (List) and dictionary-like (Storage) data structures that do not raise errors when an element is not found.
First download web2py's source, then copy-paste the gluon package folder into your python installation's site-packages.
Now try it out:
>>> from gluon.storage import List
>>> L = List(['a','b','c'])
>>> print L(2)
c
>>> print L(3) #No IndexError!
None
Note, it can also behave like a regular list as well:
>>> print L[3]
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
l[3]
IndexError: list index out of range
1 Comment
def __call__(self, idx, df=None): return self[idx] if 0<=idx<len(self) else dfThere is no built-in way to do what you want to do.
Here is a good post that may help you: Why list doesn't have safe "get" method like dictionary?
1 Comment
IndexError: list index out of range problem, not the index() method with ValueError: <value> is not in list issue described here.def index(li, element):
return next((i for i, e in enumerate(li) if e == element), -1)
This one-liner function returns -1 if not found. No going through twice, no additional copy, and Pythonic.
2 Comments
You could use list[i].find(x) instead where i is a element inside a matrix such as string representation of an array. It returns -1 when mismatched or the start of the index of the matched string.
Example:
P = ["555", "333"]
G = ["333"]
G[0].find(P[1]) #returns starting index 0
G[0].find(P[0]) #returns -1 because no match
4 Comments
i come from? That's the result that list.index(x) is supposed to return.find(x) assumes that the list elements and x are strings. This will return the index of x as a substring in list[i], not the index of x in list.P = ["555", "3"]; G = ["333"]. This shouldn't find a match, but it will return 0 in the first example.
i == -1and take some action.None. It's often useful for a missing index to throw, yes, but if[][0]throws, I would also like[].index(0)to returnNone, or at least allow[].index(0, default=None). It's 1 line instead of 4.