str.index()
Identical to find, except for the failure mode: find returns -1, index raises. Pick the one whose failure you actually want to handle.
Demo
index scans left to right and returns the first position where sub starts. With a start argument the search skips ahead, but the number you get back is still counted from the beginning of the string. The one real difference from find is the failure case: a missing substring raises ValueError rather than returning -1. The empty string is a curiosity — it is considered present everywhere, so it returns start immediately.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| sub | str | yes | Substring to look for. The empty string is found immediately at position start. |
| start | int | no (0) | Where to begin searching. The returned index is still absolute, not relative to start. |
| end | int | no (len) | Where to stop. The slice searched is s[start:end]. |
Return value
int — Lowest index where sub is found. Raises ValueError if it is absent — it never returns -1.
Common patterns
i = line.index(":") key, value = line[:i], line[i + 1:]
try: start = doc.index("<body>") except ValueError: raise ParseError("no body tag")
i = 0 while True: try: i = s.index(sub, i) + 1 except ValueError: break
Examples
Pitfalls
i = 'hello'.index('z')
i = 'hello'.find('z') if i == -1: ...
s = 'hello' s[:s.find('z')]
i = s.find('z') result = s[:i] if i != -1 else s
'hello'.index('l', 3)
'hello'.index('l', 3) - 3
When to use
- The substring is required, and absence is a genuine error
- You want the failure to stop the code rather than flow onward
- Parsing where a missing marker means malformed input
- Absence is normal and expected → find, which returns -1
- You only need a yes/no answer → the in operator
- Searching from the right → rindex or rfind
Notes
FAQ
When absence is a bug rather than a case to handle. index turns a missing substring into an immediate, loud ValueError; find hands back -1 and trusts you to check. If you would write "if i == -1: raise" anyway, index already does it.
i = line.index(':') # a line without a colon is malformed