24

In Python, find and index are very similar methods, used to look up values in a sequence type. find is used for strings, while index is for lists and tuples. They both return the lowest index (the index furthest to the left) that the supplied argument is found.

For example, both of the following would return 1:

"abc".find("b")
[1,2,3].index(2)

However, one thing I'm somewhat confused about is that, even though the two methods are very similar, and fill nearly the same role, just for different data types, they have very different reactions to attempting to find something not in the sequence.

"abc".find("d")

Returns -1, to signify 'not found', while

[1,2,3].index(4)

raises an exception.

Basically, why do they have different behaviors? Is there a particular reason, or is it just a weird inconsistency for no particular reason?

Now, I'm not asking how to deal with this – obviously, a try/except block, or a conditional in statement, would work. I'm simply asking what the rationale was for making the behavior in just that particular case different. To me, it would make more sense to have a particular behavior to say not found, for consistency's sake.

Also, I'm not asking for opinions on whether the reason is a good reason or not – I'm simply curious about what the reason is.

Edit: Some have pointed out that strings also have an index method, which works like the index method for lists, which I'll admit I didn't know, but that just makes me wonder why, if strings have both, lists only have index.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Kevin
  • 1,870
  • 2
  • 20
  • 22
  • 2
    Note that `str.index` exists, with analogous functionality to `list.index`. – Robᵩ Dec 05 '13 at 20:55
  • the docs' description for `str.index` is "Like find() but raise ValueError when the substring is not found." so the answer may just be that there's no equivalent for `find` in lists. – roippi Dec 05 '13 at 20:55
  • 2
    One difference between the two methods is that you can find substrings: `'abc'.find('bc')` – Donald Miner Dec 05 '13 at 20:56
  • Oh, I didn't realize that strings had an index method too, that's interesting. – Kevin Dec 05 '13 at 20:58
  • But, even if strings have an index method, they could've given lists one too, so that the two pairs would have the same functionality, at least. – Kevin Dec 05 '13 at 21:01
  • Keep in mind that Strings can be thought of as a type of specialized sequence. As Donald Miner mentioned, one of the benefits of 'find' is the ability to use sub-strings as your search param, which index does not allow. – K.Niemczyk Dec 05 '13 at 21:06
  • 5
    @DonaldMiner string's index also happily deals with it, cf `'abc'.index('bc')` – alko Dec 05 '13 at 21:19

1 Answers1

26

This has always been annoying ;-) Contrary to one answer, there's nothing special about -1 with respect to strings; e.g.,

>>> "abc"[-1]
'c'
>>> [2, 3, 42][-1]
42

The problem with find() in practice is that -1 is in fact not special as an index. So code using find() is prone to surprises when the thing being searched for is not found - it was noted even before Python 1.0.0 was released that such code often went on to do a wrong thing.

No such surprises occur when index() is used instead - an exception can't be ignored silently. But setting up try/except for such a simple operation is not only annoying, it adds major overhead (extra time) for what "should be" a fast operation. Because of that, string.find() was added in Python 0.9.9 (before then, only string.index() could be used).

So we have both, and that persists even into Python 3. Pick your poison :-)

Tim Peters
  • 67,464
  • 13
  • 126
  • 132
  • 3
    This is weirdly enlightening yet disappointing at the same time! – Thomas Orozco Dec 05 '13 at 21:24
  • 1
    i usually use find() when I don't know if the value is there or not (and so, i would check the result manually i.e. LYBL type programming style), and index() if i expect the value to be there (and then throw an exception EAFP style if it isn't). edit: point is, they are optimized differently to support different styles/purposes of coding. – Corley Brigman Dec 05 '13 at 21:24
  • 1
    @ThomasOrozco, sometimes there just isn't a wonderful answer! ;-) People on python-dev have been *trying* to find one for sequence search, off and on, for decades. Nothing so far has seemed better than the current weirdly unsatisfying `index/find` approach. – Tim Peters Dec 05 '13 at 21:27
  • 3
    Since `-1` is a valid index, I wonder why they chose it to mean "substring not found". Wouldn't it be clearer to have it return `None`? After all, "explicit is better than implicit". How is `-1` more explicit than `None` with "substring not found"? –  Dec 05 '13 at 21:27
  • @iCodez, yuck! ;-) Then lots of code would confuse a "found it at index 0" result with `False` or `None`. Note in particular that `0 == False` is true, and that `False` works the same as `0` as an index! If you're going to return a special value, it's as easy to check for `< 0` as for anything else, and at least the return type is wholly predictable then. The problem with any non-exception scheme is that people plain forget to check. – Tim Peters Dec 05 '13 at 21:31
  • @iCodez why stop there, why not `ValueError`? :) I assume the reason for -1 as the "not found" sentinel comes from users of languages like Java which return that for `indexOf` not-founds. Of course, negative indexing is not a thing in such languages, so its use in python is questionable. – roippi Dec 05 '13 at 21:32
  • @roippi - I like your idea at the end. However, throwing a `ValueError` would be bad because one of the features of `str.find` is that it doesn't throw an error if the string can't be found. –  Dec 05 '13 at 21:35