Tuesday, May 6, 2008

A good python Rational class

Python implements integers, floats, decimals and complex numbers. But no rationals. PEP 239, "Adding a Rational Type to Python", suggests ... the addition of a rational type to python. But alas. Guido said no. And that was 2001.

So developers everywhere implement their own. Duration math depends crucially on rational arithmetic and so Víctor and I are no exception. My first implementation was in 2005, I think Víctor followed soon after, and we've shared a relatively robust implementation this year. And, just this week, we've reimplemented yet again.

Some features.

The initializer requires numerator but leaves denominator optional.

>>> p = Rational(13, 8)
>>> p
13/8

>>> q = Rational(2)
>>> q
2

Unary negation, inversion and absolute value work the way you think they do.

>>> -p
-13/8

>>> ~p
8/13

>>> abs(p)
13/8

So do the inequalities.

>>> p < q, p <= q, p == q, p >= q, p > q
(True, True, False, False, False)

Integers work too.

>>> p
13/8

>>> p < 2, p <= 2, p == 2, p > 2, p >= 2
(True, True, False, False, False)

Arithmetic __add__, __sub__, __mul__, __div__ and even __truediv__ all work.

>>> p, q
(13/8, 2)

>>> p + q
29/8

>>> p - q
-3/8

>>> p * q
13/4

>>> p / q
13/16

Things that are supposed to be commutative are. Others aren't.

>>> p + q == q + p, p * q == q * p
(True, True)

>>> p - q == q - p, p / q == q / p
(False, False)

Right-operators __radd__, __rsub__, __rmul__, __rdiv__ and __rtruediv__ make integers work here too.

>>> p
13/8

>>> 2 + p
29/8

>>> 2 - p
-3/8

>>> 2 * p
13/4

>>> 2 / p
16/13

Floor division comes up every once in a while.

>>> Rational(93, 8) // 2
5

And you can compose with rational mod.

>>> Rational(93, 8) % 2
13/8

>>> Rational(93, 8) % Rational(2, 3)
7/24

>>> 93 % Rational(2, 3)
1/3

Coercion works for int and float

>>> p
13/8

>>> int(p)
1

>>> float(p)
1.625

And there're set and copy methods.

>>> p.set(15, 16)
>>> p
15/16

>>> p.copy( )
15/16
>>> id(p) == id(_)
False

The code is here. If you like -- or find bugs -- let us know.

No comments: