# [Tutor] Fraction Class HELP ME PLEASE!

Cameron Simpson cs at zip.com.au
Fri Aug 7 10:08:48 CEST 2015

```On 07Aug2015 03:09, Quiles, Stephanie <stephanie.quiles001 at albright.edu> wrote:
>Hello again Cameron,
>
>Thank you, your reply is most helpful. Responses and updated code
>below. Thoughts would be appreciated. The assignment has been sent
>as my deadline was 11pm EST. But i was able to implement your
>suggestions and tips right before i submitted. responses are below
>and here is the final code i submitted for grading.

Hmm. Did you program run with successful tests? because there are some
problems...

>    def __str__(self):
>        if self.den == 1:
>            return str(self.num)
>        if self.num == 0:
>            return str(0)
>        return str(self.num) + "/" + str(self.den)

Nicer to the eye. Thanks.

>    def simplify(self):
>        common = gcd(self.num, self.den)
>        self.num = self.num // common
>        self.den = self.den // common
>
>    def show(self):
>        print(self.num, "/", self.den)
>
>    def __add__(self, otherfraction):
>        newnum = self.num * otherfraction.den + self.den * otherfraction.num
>        newden = self.den * otherfraction.den
>        common = gcd(newnum, newden)
>        return Fraction(newnum // common, newden // common)

In principle you could change the last two lines above to:

F = Fraction(newnum, newden)
F.simplify()
return F

That would let you reuse the code from the simplify() method, eliminating risk
of not getting those two things the same (and therefore getting one wrong).

Alternatively, you could have the .__init__() method call .simplify() as its
final action, meaning you would not need to worry about it when constructing a
new Fraction. However, that is a policy decision - perhaps you want a Fraction
to have exactly the original numerator/denominator unless the user explicitly
wishes otherwise.

>    def __iadd__(self, otherfraction):
>        if isinstance(otherfraction):

isinstance() takes two arguments: the object being examines and a class to see
if the object is an instance of the class. BTW, why are you doing this test? I
would guess it is so that you can have otherfraction be either a Fraction or
some other kind of numeric value.

>            return self__iadd__(otherfraction)

This isn't right either. No "." for one thing. It is syntacticly value, as it
tries to call a function named "self__iadd__". But it will fail it runtime.

It looks to me like this is incomplete. This function looks like you want to
handle a Fraction or a numeric value. However, the half for the non-Fraction
value is not present and the half for the Fraction is wrong.

>    def __sub__(self, otherfraction):
>        newnum = self.num * otherfraction.den - self.den * otherfraction.num
>        newden = self.den * otherfraction.den
>        common = gcd(newnum, newden)
>        return Fraction(newnum // common, newden // common)
>
>    def __mul__(self, otherfraction):
>        newnum = self.num * otherfraction.num * self.den * otherfraction.den
>        newden = self.den * otherfraction.den
>        common = gcd(newnum, newden)
>        return Fraction(newnum // common, newden // common)

Here you're doing the greatest common factor thing again. This adds to the
argument for reusing the .simplify() method here instead of wring out that
arithmetic again. This is a situation you will encounter often, and it often
gets called "DRY": don't repeat yourself. Just some more terminology.

The other benefit to embedding this in .simplify() is that someone reading the
code (including yourself much later) can see this:

F.simplify()

and know what's happening. Otherwise they need to read the arithmetic and
figure out what it is for.

>    def __imul__(self, otherfraction):
>        if isinstance(otherfraction):
>            return self__mul__(otherfraction)

Same issues here as with __iadd__.

[...] snip ...]
>    def __rshift__(self, otherfraction):
>        return (self.num * self.den) >> (otherfraction.num * otherfraction.den)

This, like __xor__, is another binary operator. You can certainly define it.
But does it make sense? Again, I would personally be reluctant to define
__xor__ on Fractions.

>""" This program will test the following operators, addition, subtraction, multiplication,
>division, true division, exponentiation, radd, modulo, shifting, ordering (less than, greater , equal to, different than,
>same as). All using fractions. """

I would put this docstring at the top of the program, and possibly the main()
function as well.

Actually, I would do two things: have a nice big opening comment or docstring
at the top of the program briefly describing the Fraction class and that this
mentioning program can be invoked as a "main program". The second thing is that
the docstring above, because it reads as describing the main() function, should
go _inside_ the main function. Like this:

def main():
''' This program will test the following operators, addition, ...
........
'''
F1 = ...
...

When you put a string inside a function or class, immediately under the "def"
or "class" line, that string is called a docstring as it is intended as
documentation for that function or class. The Python language actually collects
it and saves it as the .__doc__ attribute of the function or class, and you can
get it back later.

In the interactive Python interpreter (which you get when you just run the
command "python"), you can call the help() function on something and have the
docstring recited back at you. So at the python prompt you can type:

help(main)

and be told about it. By putting docstrings on your Fraction class and also on
each of its methods you make that information available via help() later.

Otherwise, good luck.

Cheers,
Cameron Simpson <cs at zip.com.au>
```

More information about the Tutor mailing list