str.rindex()

The right-hand counterpart of index. It searches from the end, but the index it returns still counts from the start.

String methodPython 1.0+Live demo
Common call
s.rindex(sub)
Returns
int — the LAST position, still counted from 0 at the left
Replaces
rfind() plus a manual "== -1" check
Watch out
the search direction reverses, the numbering does not
str.rindex(sub[, start[, end]])
int

Demo

Live evaluation
Try:
Inputs
stringstrstring to search in
substrsubstring to find
startintstart position (blank = 0)
Output
'hello'.rindex('l')
3

rindex finds the LAST place sub occurs. In "abcabc" the letter b sits at 1 and 4, so rindex returns 4 while index returns 1. Only the search direction is reversed — the number is still an ordinary left-counted index you can slice with directly. Absence raises ValueError, exactly as with index; use rfind if you would rather get -1.

Parameters

NameTypeRequiredDescription
substryesSubstring to look for. The empty string is found at the far end of the searched range.
startintno (0)Left boundary of the range searched. Matches before it are ignored.
endintno (len)Right boundary. The slice searched is s[start:end].

Return value

intHighest index where sub is found. Raises ValueError if it is absent — it never returns -1.

Common patterns

Split on the last separator
The classic use — file extensions and dotted paths split at the final dot.
i = name.rindex(".")
stem, ext = name[:i], name[i + 1:]
Take the final path segment
Everything after the last slash, with the separator dropped.
leaf = path[path.rindex("/") + 1:]
Require the separator
When a missing separator means the input is malformed, let it raise.
try:
    i = ref.rindex(":")
except ValueError:
    raise ValueError("expected host:port")

Examples

1. Last match
'hello'.rindex('l')
Returns
3
2. Last of many
'abcabc'.rindex('b')
Returns
4
3. index finds first
'abcabc'.index('b')
Returns
1 # the contrast
4. Missing raises
'hello'.rindex('z')
Returns
ValueError: substring not found
5. Empty substring
'hello'.rindex('')
Returns
5
6. rfind returns -1
'hello'.rfind('z')
Returns
-1 # the alternative

Pitfalls

1. The result is not counted from the right
Only the scan is reversed. People expect a distance from the end, or a negative index, and get an ordinary left-counted position instead.
Expected from the end
'hello'.rindex('l')
3 # not 1, and not -2
Convert if you need it
len(s) - s.rindex('l') - 1
1 # distance from the end
2. start still means the LEFT boundary
Even searching backwards, start and end describe the slice s[start:end]. They do not swap roles, so start is not "where to begin scanning from the right".
Read as a scan origin
'abcabc'.rindex('b', 2)
4 # searched s[2:], still the last hit
Bound with end instead
'abcabc'.rindex('b', 0, 4)
1
3. Raises where rfind returns -1
Same trap as index versus find. The signatures match, so swapping one for the other looks harmless until a missing substring reaches production.
Uncaught
'hello'.rindex('z')
ValueError: substring not found
rfind for optional
i = 'hello'.rfind('z')
if i == -1:
    ...
-1, handled

When to use

Use it
  • Splitting on the LAST separator — extensions, paths, host:port
  • The separator is required and absence is an error
  • You want the final occurrence and a left-counted index to slice with
Reach for something else
  • Absence is expected → rfind, which returns -1
  • You want the FIRST occurrence → index or find
  • Splitting a path → os.path or pathlib handles the edge cases for you

Notes

Complexity
O(n * m) worst case — the same algorithm as index, scanning backwards
Return
A non-negative int; never -1, because absence raises instead
CPython impl
Objects/stringlib/find.h :: stringlib_rindex
Memory
No allocation — scans in place, no reversed copy is made
Thread-safe
Yes — strings are immutable

FAQ

Because rindex returns a position, not a distance from the end. Positions in Python strings count from 0 at the left regardless of which direction you searched, which is what makes the result directly usable in a slice.

s = 'hello'
s[:s.rindex('l')]   # 'hel'

History

1.0
Present since the earliest string methods, alongside rfind.
2.5
start and end accepted as None, easing pass-through of optional arguments.