44. Fractions - part 1

Would it not be nice if we could use Python to add and multiply fractions just like we can do it with integers and other number types? Something like:

>>> a = 1/2
>>> b = 1/4
>>> print a + b
3/4

Actually, we can teach Python to do something just like that! This is what we are going to learn to do in this multi-part lesson. It is going to be a rather long lesson, but we are going to learn a fair bit about some very special parts of Python.

To help prevent having bugs in our program, we will proceed systematically, adding only a little bit of new code at a time, using a three step approach:

  1. Add some new functionality.
  2. Check that it gives us the desired result, testing with print statements.
  3. Replace the print statements by assert statements.

Repeat these 3 steps as often as required! This is one example where it make sense to repeat yourself!


Creating a new type

Without further ado, here's the basic code to define a new "fraction" type, and test it.

  1 class Fraction(object):
  2     def __init__(self, numerator, denominator=1):
  3         self.num = numerator
  4         self.denom = denominator
  5 
  6 #== testing area below===
  7 
  8 if __name__ == "__main__":
  9     a = Fraction(1, 2)
 10     b = Fraction(3)
 11     print a.num, a.denom
 12     print b.num, b.denom
 13     print a
 14     print b

By now, you should be able to understand what this code does on your own. Notice the use of a named argument on line 2 so that we could pass a single argument to Fraction() [as we did on line 10] and it would interpret it as an integer (divided by 1). If we run it, we get the following output:

1 2
3 1
<__main__.Fraction object at 0x01530F30>
<__main__.Fraction object at 0x01530F50>

The first two lines are as expected. However, the last two, when we try to print the fractions a and b do not exactly look like what we would want. Python does not know how we want the object to appear, so it gives us something like the variable name it uses itself to keep track of these two objects. To make Python print the fractions the way we want, we need to tell it using one of the special methods.


Representing objects as strings

To tell Python how we want objects to be printed, or represented as strings, we use the function __str__().

  1 class Fraction(object):
  2     def __init__(self, numerator, denominator=1):
  3         self.num = numerator
  4         self.denom = denominator
  5         
  6     def __str__(self):
  7         return "(%s/%s)"%(self.num, self.denom)
  8 
  9 #== testing area below===
 10 
 11 if __name__ == "__main__":
 12     a = Fraction(1, 2)
 13     b = Fraction(3)
 14     print a
 15     print b

The result is:

(1/2)
(3/1)

Your turn

Make a change to __str__() so that if the denominator is 1, only the numerator is printed. This means that, instead of having "(3/1)", we would simply have "3"; however the result for "(1/2)" should be unchanged.


Using assert

Now that we have our desired result, we replace the print statements by assert statements.

  1 class Fraction(object):
  2     def __init__(self, numerator, denominator=1):
  3         self.num = numerator
  4         self.denom = denominator
  5         
  6     def __str__(self):
  7         return "(%s/%s)"%(self.num, self.denom)
  8 
  9 #== testing area below===
 10 
 11 if __name__ == "__main__":
 12     a = Fraction(1, 2)
 13     b = Fraction(3)
 14     assert str(a) == "(1/2)"
 15     assert str(b) == "(3/1)"

When we use assertions instead of printing, we need to compare the string representation str() to the expected result. If you run this program, it should not output anything; this is what we want! Note that I still used the format with the denominator present, "(3/1)", and not the format I asked you to define in the last exercise.

Next, we teach Python to multiply fractions!

previousSorting - home - Fractions - Part 2 next