Discussion:
[Tutor] Fraction Class HELP ME PLEASE!
Quiles, Stephanie
2015-08-06 16:55:22 UTC
Permalink
Hello All,

I need to do the following assignment. I need to know how do i hard code an example for each of the operators I am implementing? What i have so far is below? He said he does not care if we plug in some numbers or if we have user input numbers, however I am unsure of how to write a program that tests each operator? Or can i write one that tests all of them? I don’t know where to start with this. Please help!


We discussed implementing different operators in our Fraction class. Here is a link to some operators in Python (look at Section 10.3.1). https://docs.python.org/3/library/operator.html

You may implement as many of these operators as you like (such as isub, itruediv, etc.) You MUST indicate in your header information which operators you are implementing, and you MUST hard code an example for each.

def gcd(m, n):
while m % n != 0:
oldm = m
oldn = n

m = oldn
n = oldm % oldn
return n


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)

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)

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)

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

def __iadd__(self, otherfraction):
if isinstance(otherfraction):
return self__iadd__(otherfraction)

def __truediv__(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)

def __radd__(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)

def getNum(self):
return self.num

def getDen(self):
return self.den

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

_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tuto
Cameron Simpson
2015-08-06 21:44:32 UTC
Permalink
Post by Quiles, Stephanie
I need to do the following assignment. I need to know how do i hard code an
example for each of the operators I am implementing? What i have so far is
below? He said he does not care if we plug in some numbers or if we have user
input numbers, however I am unsure of how to write a program that tests each
operator? Or can i write one that tests all of them? I don’t know where to
start with this. Please help!
For someone who doesn't know where to start, you seem to have a lot of decent
looking code. Is the code below all yours? If so, a good start.

Also, please try to preserve the indentation when pasting in code; indentation
is critical in Python as you know and incorrect indentation, when not an
outright syntax error, can be a source of bugs. I'll presume your code was
originally indented correctly.

Note that in this list we _do_ like code to be pasted inline where it can be
seen directly and commented on like any other part of the message text. We're
not big on attachments or links to external things (other than doco citations
like your reference to the operators Python page).
Post by Quiles, Stephanie
You may implement as many of these operators as you like (such as isub,
itruediv, etc.) You MUST indicate in your header information which operators
you are implementing, and you MUST hard code an example for each.
"Header information" is usually an opening comment. So perhaps start the
program with text like this:

# Here is a Fraction class implementing variaous operators. It currently
# supports:
#
# + (addition)
# - (subtraction)
# * (multiplication)
# / (true division)
#

and so forth.

Regarding examples and tests, one workable approach to to make your program
work as a "main program". The idea is that is invoked as a main program it will
run the examples or tests.

As your code sits now it could be used as a module - someone could place it
into python's library tree and import it, and use your Fraction class.
However, you can of course run it directly at the command prompt, eg:

% python your-fraction-file.py

When you do that, the code is still imported as a module but with the special
name '__main__'. So what a lot of python modules do is have a "main" function
which is to be fired only in the command line circumstance, such as:

def main():
F1 = Fraction(1, 2) # 1/2
F2 = Fraction(2, 3) # 2/3
print("F1 =", F1)
print("F2 =", F2)
print("F1 + F2 =", F1 + F2)
print("F1 - F2 =", F1 - F2)
print("F1 * F2 =", F1 * F2)

In order to make your program work as both an importable module which defines
the Fraction class but does not run the main function and also as a main
program which defines the class and then runs the main function you put this
piece of boilerplate code on the very bottom of the program:

if __name__ == '__main__':
main()

which checks if you're invoking it directly from the command line. If so, run
the main function.

Hopefully that gives you an idea about hardwiring examples also.

Regarding tests, you might write a simple function like this:

def check(label, computed, expected):
ok = computed == expected
if ok:
print(label, 'OK', computed, '==', expected)
else:
print(label, 'BAD', computed, '!=', expected)
return ok

Then you might consider modifying your main program to run tests instead of
bare examples, replacing:

print("F1 + F2 =", F1 + F2)

with:

check("F1 + F2", F1 + F2, Fraction(7, 6))

Because check() returns whther the check was ok, you might even count the
number of failures for some kind of report:

fail_count = 0
...
if not check("F1 + F2", F1 + F2, Fraction(7, 6)):
fail_count += 1
... more checks ...
print(fail_count, "failures")

and so forth.

This also gets you test code so that you can test your own program for
correctness before submitting your assignment.

Feel free to return to this list with updated code and new questions.

Cheers,
Cameron Simpson <***@zip.com.au>
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
http
Cameron Simpson
2015-08-07 01:51:16 UTC
Permalink
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

You test __iadd__ by running the augmented add operation:

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:

https://docs.python.org/3/reference/datamodel.html#object.__iadd__

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:

def __iadd__(self, other):
... 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:

def __iadd__(self, other):
print("%s.__iadd__(%s)..." % (self, other))
... 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).

Added points: make your next reply adopt the interleaved style of this message,
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.

Other random code comments:

[...snip: unreplied-to text removed here...]
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.
self.num = top
self.den = bottom
return str(self.num)
return str(0)
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.
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.
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)
Here is where the earlier discussion about __add__ versus __iadd__ comes into
consideration. I would be definine __iadd__ here because it is closely related
to __add__. It would look a lot like __add__ except that instead of returning a
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:

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

def __add__(self, otherfraction):
newnum, newden = self._add(otherfraction)
return Fraction(newnum, newden)

def __iadd__(self, otherfraction):
newnum, newden = self._add(otherfraction)
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.
return self.num
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.
return (self.num / self.den) > (otherfraction.num / otherfraction.den)
return (self.num / self.den) < (otherfraction.num / otherfraction.den)
return (self.num / self.den) == (otherfraction.num / otherfraction.den)
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.
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
operators (i.e. F1+F2 calls F1.__ad__(F2)).

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.
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.
print("Add Fractions: F1 + F2=", Fraction.__add__(F1, F2))
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__?
print("Is:", Fraction.__iadd__(F1, F2))
main()
Otherwise this is all looking promising. Does it run correctly for you?

Cheers,
Cameron Simpson <***@zip.com.au>
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman
Quiles, Stephanie
2015-08-07 03:09:07 UTC
Permalink
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.


def gcd(m, n):
while m % n != 0:
oldm = m
oldn = n
m = oldn
n = oldm % oldn
return n

class Fraction:
def __init__(self, top, bottom):
self.num = top
self.den = bottom

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)

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)

def __iadd__(self, otherfraction):
if isinstance(otherfraction):
return self__iadd__(otherfraction)

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)

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

def __div__(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)

def __truediv__(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)

def __pow__(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)

def __radd__(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)




def getNum(self):
return self.num

def getDen(self):
return self.den

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)

def __mod__(self, otherfraction):
return (self.num * self.den) % (otherfraction.num * otherfraction.den)

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







""" 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. """
def main():
F1 = Fraction(1,2)
F2 = Fraction(2,3)
print("F1 = ", F1)
print("F2 = ", F2)
print("Add Fractions: F1 + F2=", Fraction.__add__(F1, F2))
print("Subtract Fractions: F1 - F2=", Fraction.__sub__(F1, F2))
print("Multiply Fractions: F1 * F2=", Fraction.__mul__(F1, F2))
print("Division with Fractions: F1 / F2=", Fraction.__div__(F1, F2))
print("True Division with Fractions: F1 / F2=", Fraction.__truediv__(F1, F2))
print("Exponentiation with Fractions: F1 ** F2=", Fraction.__pow__(F1, F2))
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("Radd:", Fraction.__radd__(F1, F2))
print("Modulo of F1 and F2(this prints the remainder):", Fraction.__mod__(F1, F2))
print("Rshift( returns F1 shifted by F2:", Fraction.__rshift__(F1, F2))


if __name__ == '__main__':
main()
Post by Cameron Simpson
thanks Cameron! Here is what i have so far… new question… how do
i test the iadd, imul, etc. operators?
F1 + F2
F1 += F2
count += 1
https://docs.python.org/3/reference/datamodel.html#object.__iadd__
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!
... update self by addition of other ...
print("%s.__iadd__(%s)..." % (self, other))
... 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.
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).
Added points: make your next reply adopt the interleaved style of this message, 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...]
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. yes i
—Yes i think i put in that blank line in error i have corrected this in my code
Post by Cameron Simpson
self.num = top
self.den = bottom
return str(self.num)
return str(0)
return str(self.num) + "/" + str(self.den)
—I have gone ahead and changed it to look more like your first style suggestion.
Post by Cameron Simpson
return str(self.num)
return str(0)
return str(self.num) + "/" + str(self.den)
s = str(self.num)
s = str(0)
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.
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.
—Corrected!
Post by Cameron Simpson
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)
Here is where the earlier discussion about __add__ versus __iadd__ comes into consideration. I would be definine __iadd__ here because it is closely related to __add__. It would look a lot like __add__ except that instead of returning a new Fraction it would overwrite .num and .den with the values for the new fraction.
—I thought about moving iadd up but left it be. I have now moved it to be right under add.
Post by Cameron Simpson
newnum = self.num * otherfraction.den + \
self.den * otherfraction.num
newden = self.den * otherfraction.den
common = gcd(newnum, newden)
return newnum // common, newden // common
newnum, newden = self._add(otherfraction)
return Fraction(newnum, newden)
newnum, newden = self._add(otherfraction)
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.
return self.num
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.
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.
return (self.num / self.den) > (otherfraction.num / otherfraction.den)
return (self.num / self.den) < (otherfraction.num / otherfraction.den)
return (self.num / self.den) == (otherfraction.num / otherfraction.den)
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.
self.num * otherfraction.den == otherfraction.num * self.den
—i changed them all to show with the *
Post by Cameron Simpson
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.
return (self.num / self.den) is (otherfraction.num / otherfraction.den)
This is undesirable. There is no "__is__" method.
I thought i was doing something wrong when __is__ did not highlight. i was trying to do the identity (a is or is not b) is_(a, b). was not sure how to implement that or even if i could?
Post by Cameron Simpson
All the __name__ methods and attributes in Python are considered part of the language, and typically and implicitly called to implement things like operators (i.e. F1+F2 calls F1.__ad__(F2)).
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.
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.
print("Add Fractions: F1 + F2=", Fraction.__add__(F1, F2))
- wanted it to call the operator rather than to show the arithmetic in the code? thought it might get me brownie points? lol
Post by Cameron Simpson
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 "**”?
—yes i knew this but i copied and pasted code and forgot to change it.
Post by Cameron Simpson
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__?
print("Is:", Fraction.__iadd__(F1, F2))
main()
Otherwise this is all looking promising. Does it run correctly for you?
— yes all of them run except the iadd. i am working on it now!
Post by Cameron Simpson
Cheers,
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.o
Cameron Simpson
2015-08-07 08:08:48 UTC
Permalink
Post by Quiles, Stephanie
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...
Post by Quiles, Stephanie
return str(self.num)
return str(0)
return str(self.num) + "/" + str(self.den)
Nicer to the eye. Thanks.
Post by Quiles, Stephanie
common = gcd(self.num, self.den)
self.num = self.num // common
self.den = self.den // common
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)
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.
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.
Post by Quiles, Stephanie
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.
Post by Quiles, Stephanie
newnum = self.num * otherfraction.den - self.den * otherfraction.num
newden = self.den * otherfraction.den
common = gcd(newnum, newden)
return Fraction(newnum // common, newden // common)
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.
Post by Quiles, Stephanie
return self__mul__(otherfraction)
Same issues here as with __iadd__.

[...] snip ...]
Post by Quiles, Stephanie
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.
Post by Quiles, Stephanie
""" 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 <***@zip.com.au>
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

Loading...