In part of my code I can subtract fractions, however if I enter (- p) where p is a fraction I get a TypeError: unsupported operand type(s) for +: "Fraction" and "Fraction"
def gcd(denominator, numerator):
if numerator == 0:
return denominator
else:
return gcd(numerator, (denominator % numerator))
class Fraction:
def __init__(self,numerator = 0, denominator = 1):
self.numerator = int(numerator / gcd(abs(denominator),abs(numerator) ))
self.denominator = int(denominator / gcd(abs(denominator),abs(numerator) ))
if self.denominator < 0:
self.denominator = abs(self.denominator)
self.numerator = -1*self.numerator
elif self.denominator == 0:
raise ZeroDivisionError
def __str__(self):
if self.denominator == 1:
return str(self.numerator)
else:
return str(self.numerator) + "/" + str(self.denominator)
def __rsub__(self,other):
return self.__sub__(other)
def __sub__(self,other):
if type(other) == int:
other = Fraction(other,1)
return self.sub(other)
else:
return self.sub(other)
def sub(self,other):
result = Fraction()
result.numerator = other.denominator * self.numerator - self.denominator * other.numerator
result.denominator = other.denominator * self.denominator
multiple = gcd(result.denominator,result.numerator)
result.numerator = int(result.numerator / multiple)
result.denominator = int(result.denominator / multiple)
return result
p = Fraction(2,3)
r = (- p)
However, when my input is (1 - p) I get the correct output. Suppose p = Fraction(2, 3) then I would like (- p) to return (-2 / 3) or (2/ -3). The problem seems to me to be in the fact that no input is given for the first argument when subtracting. While searching I did come across things like __neg__
, but I'm still new to python and using classes so I don't know exactly how to implement this. Does anyone know how I can fix this?
Thanks in advance!
You are right; you need to implement __neg__
because that minus is not the (binary) subtraction operator, but the unary minus.
Here is how you can do it:
def __neg__(self):
return Fraction(-self.numerator, self.denominator)
__rsub__
You need to change the implementation of __rsub__
, because that method will be called when other
does not support __sub__
. For instance, it will kick in when you evaluate this:
p = 1 - Fraction(2, 3)
That evaluation will not work as it currently stands. You need to have this:
def __rsub__(self, other):
return Fraction(other) - self
or, explicitly calling __sub__
:
def __rsub__(self, other):
return Fraction(other).__sub__(self)
The constructor correctly normalises the fraction, making sure the denominator is positive, but you don't do the same in the sub
method. There the result may not be normalised: the denominator could remain negative.
Moreover, it is a pity that you duplicate the gcd-related code that is already in the constructor. It is better to rely on the constructor for that logic.
It is better to treat instances as immutable. So you should not have any assignments to instance.numerator
and instance.denominator
outside of the constructor. Make sure to first determine the numerator and denominator (without normalisation), and then call the constructor passing these as arguments.
You may want to compare Fractions for equality or relative order. For that you can implement __eq__
, __lt__
, ...etc.
Here is how I would do it:
def gcd(denominator, numerator):
if numerator == 0:
return denominator
else:
return gcd(numerator, denominator % numerator)
class Fraction:
def __new__(cls, numerator=0, denominator=1):
if isinstance(numerator, Fraction):
return numerator # Use this instance and ignore 2nd argument
return super(Fraction, cls).__new__(cls)
def __init__(self, numerator=0, denominator=1):
if isinstance(numerator, Fraction):
return # self is already initialised by __new__
if denominator == 0:
raise ZeroDivisionError
div = gcd(abs(denominator), abs(numerator))
if denominator < 0:
div = -div
self.numerator = numerator // div
self.denominator = denominator // div
def __str__(self):
if self.denominator == 1:
return str(self.numerator)
else:
return f"{self.numerator}/{self.denominator}"
def __rsub__(self, other):
return Fraction(other) - self
def __sub__(self, other):
other = Fraction(other)
return Fraction(other.denominator * self.numerator
- self.denominator * other.numerator,
other.denominator * self.denominator)
def __neg__(self):
return Fraction(-self.numerator, self.denominator)
def __eq__(self, other):
other = Fraction(other)
return (self.numerator == other.numerator and
self.denominator == other.denominator)
def __lt__(self, other):
other = Fraction(other)
return (other.denominator * self.numerator <
self.denominator * other.numerator)
def __gt__(self, other):
return Fraction(other) < self
def __le__(self, other):
return self < other or self == other
def __ge__(self, other):
return self > other or self == other