# [Tutor] Fraction Class HELP ME PLEASE!

Cameron Simpson cs at zip.com.au
Fri Aug 7 03:51:16 CEST 2015

```On 06Aug2015 23:50, Quiles, Stephanie <stephanie.quiles001 at albright.edu> wrote:
>thanks Cameron! Here is what i have so far… new question… how do
>i test the iadd, imul, etc. operators?

Like the others, by firing them. You test __add__ by running an add between two
expressions:

F1 + F2

F1 += F2

and so forth. The "i" probably comes from the word "increment" as the commonest
one of these you see is incrementing a counter:

count += 1

They're documented here:

The important thing to note is that they usually modify the source object. So:

F1 += F2

will modify the internal values of F1, as opposed to __add__ which returns a
new Fraction object.

>Hopefully this time the
>indents show up.

Yes, looks good.

>And yes the beginning code came out of our text
>book I added on some functions myself but they give you a big chunk
>of it.

I thought it looked surprisingly complete given your questions. It's good to be
up front about that kind of thing. Noone will think less of you.

>I am hoping that this is what the professor was looking for.
>I want to add some more operators but am unsure how to put them in
>or test them? For example I want to add __ixor__, itruediv, etc.
>Any other suggestions would be great!

Adding them is as simple as adding new methods to the class with the right
name, eg:

... update self by addition of other ...

If you want to be sure you're running what you think you're running you could
put print commands at the top of the new methods, eg:

... update self by addition of other ...

Obviously you would remove those prints once you were satisfied that the code
was working.

Adding __itruediv__ and other arithmetic operators is simple enough, but
defining __ixor__ is not necessarily meaningful: xor is a binary operation
which makes sense for integers. It needn't have a natural meaning for
fractions. When you define operators on an object it is fairly important that
they have obvious and natural effects becauase you have made it very easy for
people to use them.  Now, you could _define_ a meaning for xor on fractions,
but personally that is one I would not make into an operator; I would leave it
as a normal method because I would want people to think before calling it.

The point here being that it is generally better for a program to fail at this
line:

a = b ^ c     # XOR(b, c)

because "b" does not implement XOR than for the program to function but quietly
compute rubbish because the user _thoght_ they were XORing integers (for
example).

where you reply point by point below the relevant text. It makes discussions
read like conversations, and is the preferred style in this list (and many
other techincal lists) because it keeps the response near the source text. Hand
in hand with that goes trimming irrelevant stuff (stuff not replied to) to keep
the content shorter and on point.

[...snip: unreplied-to text removed here...]
>def gcd(m, n):
>    while m % n != 0:
>        oldm = m
>        oldn = n
>
>        m = oldn
>        n = oldm % oldn
>    return n

It reads oddly to have a blank line in the middle of that loop.

>class Fraction:
>    def __init__(self, top, bottom):
>        self.num = top
>        self.den = bottom
>
>    def __str__(self):
>        if self.den == 1:
>            return str(self.num)
>        elif self.num == 0:
>            return str(0)
>        else:
>            return str(self.num) + "/" + str(self.den)

While your __str__ function will work just fine, stylisticly it is a little
odd: you're mixing "return" and "elif". If you return from a branch of an "if"
you don't need an "elif"; a plain old "if" will do because the return will
prevent you reaching the next branch. So that function would normally be
written in one of two styles:

Using "return":

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)

or using "if"/"elif"/...:

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

For simple things like __str__ the former style is fine. For more complex
functions the latter is usually better because your code does not bail out half
way through - the return from the function is always at the bottom.

>    def simplify(self):
>        common = gcd(self.num, self.den)
>
>        self.num = self.num // common
>        self.den = self.den // common

Again, I would personally not have a blank line in th middle of this function.

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

consideration. I would be definine __iadd__ here because it is closely related
new Fraction it would overwrite .num and .den with the values for the new
fraction.

If Addition were complex (and fractional addition is near this border for me) I
might define a "private" called ._add to compute the new numerator and
denominator, and then define __add__ and __iadd__ in terms of it, untested
example:

newnum = self.num * otherfraction.den + \
self.den * otherfraction.num
newden = self.den * otherfraction.den
common = gcd(newnum, newden)
return newnum // common, newden // common

return Fraction(newnum, newden)

self.num = newnum
self.den = newdem

You can see that this (a) shortens the total code and (b) guarentees that
__add__ and __iadd__ perform the same arithmetic, so that they cannot diverge
by accident.

The shared method _add() is a "private" method. In Python this means only that
because its name begins with an underscore, other parts of the code (outside
the Fraction class itself) are strongly discouraged from using it: you, the
class author, do not promise that the method will not go away or change in the
future - it is part of the arbitrary internal workings of your class, not
something that others should rely upon.

This is a common convention in Python - the language does not prevent others
from using it. Instead we rely on authors seeing these hints and acting
sensibly. By providing this hint in the name, you're tell other users to stay
away from this method.

>    def getNum(self):
>        return self.num
>
>    def getDen(self):
>        return self.den

These two methods are hallmarks of "pure" object oriented programming. A pure
OO program never accesses the internal state of antoher object directly and
instead calls methods like getNum() above to ask for these values. This lets
class authors completely change the internals of a class without breaking
things for others.

However, in Python it is more common to make the same kind of distinction I
made earlier with the ._add() method: if an attribute like .num or .den does
not have a leading underscore, it is "public" and we might expect other users
to reach for it directly.

So we might expect people to be allowed to say:

F1.num

to get the numerator, and not bother with a .getNum() method at all.

If there are other attributes which are more internal we would just name them
with leaing underscores and expect outsiders to leave them alone.

>    def __gt__(self, otherfraction):
>        return (self.num / self.den) > (otherfraction.num / otherfraction.den)
>
>    def __lt__(self, otherfraction):
>        return (self.num / self.den) < (otherfraction.num / otherfraction.den)
>
>    def __eq__(self, otherfraction):
>        return (self.num / self.den) == (otherfraction.num / otherfraction.den)
>
>    def __ne__(self, otherfraction):
>        return (self.num /self.den) != (otherfraction.num /otherfraction.den)

These looke like normal arithmetic comparison operators.

I notice that you're comparing fractions by division. This returns you a
floating point number. Floating point numbers are _not_ "real" numbers.
Internally they are themselves implemented as fractions (or as scientific
notation - a mantissa and an exponent - semanticly the same thing). In
particular, floating point numbers are subject to round off errors.

You would be safer doing multiplecation, i.e. comparing:

self.num * otherfraction.den == otherfraction.num * self.den

which will produce two integers. Python uses bignums (integers of arbitrary
size) and some thing should never overflow, and is _not subject to round off
errors.

When you use division for the comparison you run the risk that two Fractions
with very large denominators and only slightly different numerators might
appear equal when they are not.

>    def __is__(self, otherfraction):
>        return (self.num / self.den) is (otherfraction.num / otherfraction.den)

This is undesirable. There is no "__is__" method.

All the __name__ methods and attributes in Python are considered part of the
language, and typically and implicitly called to implement things like

You should not make up __name__ methods: they are reserved for the language.
There are at least two downsides/risks here: first that you think this will be
used, when it will not - the code will never be run and you will wonder why
your call does not behave as you thought it should. The second is that some
furture update to the language will define that name and it will not mean what
you meant when you used it. Now your code _will_ run, but not do what a user
expects!

BTW, the jargon for the __name__ names is "dunder": .__add__ is a "dunder
method"; derived from "double underscore", which I hope you'd agree is a
cumbersome term.

>def main():
>    F1 = Fraction(1,2)
>    F2 = Fraction(2,3)
>    print("F1 = ", F1)
>    print("F2 = ", F2)

print() adds a space between the comma separate items, you don't need to
include one inside the quotes.

Any reason you've not fired this implicitly? Like this:

print("Add Fractions: F1 + F2=", F1 + F2)

Actually, you can make a case for doing both in your main program to show that
they do the same thing.

>    print("Subtract Fractions: F1 - F2=", Fraction.__sub__(F1, F2))
>    print("Multiply Fractions: F1 * F2=", Fraction.__mul__(F1, F2))
>    print("True Division with Fractions: F1 / F2=", Fraction.__truediv__(F1, F2))
>    print("Exponentiation with Fractions: F1 // F2=", Fraction.__pow__(F1, F2))

Shouldn't this be "**"?

>    print("Is F1 Greater than F2?:", Fraction.__gt__(F1, F2))
>    print("Is F1 less than F2?:", Fraction.__lt__(F1, F2))
>    print("Is F1 Equal to F2?:", Fraction.__eq__(F1, F2))
>    print("Is F1 different than F2?:", Fraction.__ne__(F1, F2))
>    print ("Is F1 same as F2?:", Fraction.__is__(F1, F2))

Here you want to avoid this. Firstly, Python has an "is" operator, and it does
not have a dunder method. Secondly, in what way does your __is__ differe from
__eq__?