fractions: Whole-Number Fraction Still Can't Be Used as an Index (tested)

A Fraction that reduces to a whole number, like Fraction(4, 1), looks like it should work anywhere an integer is expected -- it prints as 4, and int(Fraction(4, 1)) == 4. We measured what happens when such a value is used directly as a list index, a range() bound, or with operator.index().

1. A whole-number Fraction can't index a list, even though its value is an integer

from fractions import Fraction

lst = [10, 20, 30, 40, 50]
try:
    print(lst[Fraction(2, 1)])
except TypeError as e:
    print("TypeError:", e)
TypeError: list indices must be integers or slices, not Fraction

Measured: Fraction(2, 1) is numerically 2 -- Fraction(2, 1) == 2 is True -- but using it directly as a list index raises TypeError regardless.

2. range() and operator.index() reject it the same way

from fractions import Fraction
import operator

try:
    list(range(Fraction(3, 1)))
except TypeError as e:
    print("range TypeError:", e)

try:
    operator.index(Fraction(4, 1))
except TypeError as e:
    print("operator.index TypeError:", e)
range TypeError: 'Fraction' object cannot be interpreted as an integer
operator.index TypeError: 'Fraction' object cannot be interpreted as an integer

Measured: both range(Fraction(3, 1)) and operator.index(Fraction(4, 1)) raise the same TypeError, with the exact message Python uses for any type that doesn't implement __index__. Fraction doesn't implement it at all -- not even when the denominator happens to be 1.

3. int() conversion works fine -- the gap is specifically the __index__ protocol

from fractions import Fraction

f = Fraction(2, 1)
print(int(f))
lst = [10, 20, 30]
print(lst[int(f)])
2
30

Measured: explicit int(f) conversion works fine and produces a usable index -- the problem isn't that the value can't become an integer, it's that Fraction never implements __index__, the specific protocol Python uses for implicit integer contexts like list indexing, slicing, and range(). This is different from, say, bool, which is a true int subclass and works as an index automatically. Code that does exact arithmetic with Fraction to avoid floating-point error, and expects a result that lands on a whole number to behave like one everywhere, will hit TypeError the moment that result is used as an index or range() bound -- even though printing it, comparing it with ==, or doing further arithmetic with it all work normally. Call int() explicitly before using a Fraction result in any indexing or range context.

Frequently asked questions

Can a Fraction be used as a list index if its value is a whole number?

No -- we measured lst[Fraction(2, 1)] raising TypeError: list indices must be integers or slices, not Fraction, even though Fraction(2, 1) == 2 is True. Fraction never implements the __index__ protocol, regardless of its denominator.

Why does range(Fraction(3, 1)) raise TypeError?

range() requires its arguments to support the __index__ protocol, which Fraction doesn't implement at all. We confirmed this raises 'Fraction' object cannot be interpreted as an integer even when the Fraction's value is a whole number.

How do I use a Fraction as a list index or range argument?

Convert it explicitly with int() first -- we measured int(Fraction(2, 1)) returning 2 and lst[int(Fraction(2, 1))] working normally. Fraction supports explicit int() conversion but not the implicit __index__ protocol that indexing and range() rely on.

Is Fraction's lack of __index__ support different from how bool works as an index?

Yes -- bool is a true subclass of int, so it automatically satisfies __index__ and works as a list index (True == 1). Fraction is not an int subclass and implements no __index__ method at all, so even a Fraction that equals a whole number is rejected.

Written with AI assistance. Measured on 2026-10-04 with Python 3.13.5's built-in fractions module: used a Fraction with denominator 1 (a mathematically whole number) as a list index, a range() argument, and with operator.index(), and compared to converting it with int() first. PlainHeaders is our extension; Python's fractions module is built in.