[Tutor] Fraction class

Norvell Spearman norvell@houseofspearman.org
Wed Mar 19 12:12:10 2003


These questions are related to ``How to Think Like a Computer Scientist:
Learning with Python,'' Appendix B ``Creating a new data type.''  This
part of the book shows how to overload operators so that expressions
like

    >>> print Fraction(1, 2) + Fraction(3, 4)  # 1/2 + 3/4 = ?

output the expected results (5/4 in this case).  The authors show how to
implement fraction addition, multiplication, and comparison.  They leave
the following as exercises:  subtraction (__sub__ and __rsub__),
division (__div__ and __rdiv__), exponentiation, and the ability to use
long integers for numerators and denominators.

I've got subtraction and division working with no problem, but the
exponentiation is posing some difficulties for me.  From the text:

    We can compute powers by overriding __pow__, but the implementation
    is a little tricky.  If the exponent isn't an integer, then it may
    not be possible to represent the result as a Fraction.  For example,
    Fraction(2)**Fraction(1, 2) is the square root of 2, which is an
    irrational number (it can't be represented as a fraction).  So it's
    not easy to write the most general version of __pow__.

So would it be best to just have Python raise an exception when it's
given a fractional exponent?  There are, of course, fractions (or
otherwise) to fractional powers that return fractions ((4/9)**(1/2),
(1/27)**(2/3), for example) but I'm not sure how to handle these cases.
If I convert the numerators and denominators to floats and do regular
exponentiation, how can I be sure I'll get back to the correct int/int
representation of the fraction?  For example:

    >>> (3**(1./3.))**3
    2.9999999999999996

If the result had been 3.0000000000000001 I could convert it to int and
get the right answer.  If I convert 2.999999... to int, though, I get 2.

The other part of the exercise is handling long ints as numerators and
denominators.  I tried ``print Fraction(1L, 2L)'' with just the code
given in the book and got ``1/2'' as a result.  Earlier in this same
book the authors try to give an example of regular int overflow but
their example is handled gracefully by the version of Python I'm using
(2.2.2) whereas the book's solution requires extra code to handle long
ints.  So the only reason I'd need to include code for long ints in this
Fraction class is for backwards compatibility, right?

Thanks in advance for any answers to this and all apologies for being
long-winded.

-- 
Norvell Spearman