PEP 3141: __ratio__ instead of numerator/denominator

Ok, here is a more complete proposal:

  1. Define a new special method __ratio__ with the following meaning: q.__ratio__() must be a 2-tuple (n, d) of integers such that q * d == n. The objects n and d don’t have to be Python ints, but they must support __index__.

  2. Within the standard library, int, fractions.Fraction, float and decimal.Decimal would support __ratio__. For the latter two, this would be an alias of as_integer_ratio. Note that __ratio__ is therefore not limited to numbers.Rational, it can be used also for exact arithmetic with floating point numbers. This may be useful for the time module, see bpo-35707.

  3. The specification of numbers.Rational would be changed to say that rationals must have such a __ratio__ method. The numerator and denominator properties are no longer required (but the expectation is that existing classes will keep them).

  4. numbers.Rational would have a default implementation of __ratio__ returning (self.numerator, self.denominator). Note that this automatically makes __ratio__ work for fractions.Fraction and all other existing classes inheriting from numbers.Rational.

  5. A helper function operator.ratio(x) is added, returning x.__ratio__() but falling back to (x.numerator, x.denominator). This is recommended over calling __ratio__ manually. If it’s deemed useful, also a C API function will be added.

  6. The constructor for fractions.Fraction would use operator.ratio().

As far as I can see, this proposal is backwards compatible in the sense that all functionality that used to work still works. Nevertheless, existing code should be changed to support the new protocol:

  1. Classes registering as numbers.Rational without actually inheriting should implement __ratio__.

  2. Code checking for numbers.Rational and accessing the numerator/denominator properties should use operator.ratio() instead.

1 Like