I am attempting to write a class to help me output mixed fractions.
This code works fine for my purpose
class mixed_fraction():
from fractions import Fraction
'''Returns mixed fractions in tuple format
(whole_part, numerator, denominator)
'''
def __init__(self, numerator = 0, denominator = 1):
self.numerator = numerator
self.denominator = denominator
def mixed(self):
if self.denominator == 0:
return 'Denominator cannot be Zero'
quotient = int(self.numerator/self.denominator)
remain = self.numerator - (self.denominator * quotient)
frac = Fraction(remain, self.denominator)
return (quotient, frac.numerator, frac.denominator)
Sample inputs
print(mixed_fraction(3, 2).mixed())
print(mixed_fraction(1, 5).mixed())
print(mixed_fraction(1, 0).mixed())
print(mixed_fraction().mixed())
print(mixed_fraction(7, 2).mixed())
print(mixed_fraction(769, 17).mixed())
print(mixed_fraction(384, 256).mixed())
Outputs
(1, 1, 2)
(0, 1, 5)
Denominator cannot be Zero
(0, 0, 1)
(3, 1, 2)
(45, 4, 17)
(1, 1, 2)
But I want to be able to call out the zero error at the creation of the object because it makes no sense to create a fraction with a zero divisor in the first place. So I need help on catching that error on time.
class mixed_fraction():
from fractions import Fraction
'''Returns mixed fractions in tuple format
(whole_part, numerator, denominator)
'''
def __init__(self, numerator = 0, denominator = 1):
self.numerator = numerator
self.denominator = denominator
if denominator == 0:
return 'Denominator cannot be Zero'
def mixed(self):
quotient = int(self.numerator/self.denominator)
remain = self.numerator - (self.denominator * quotient)
frac = Fraction(remain, self.denominator)
return (quotient, frac.numerator, frac.denominator)
The problem is that __init__
must return None
. So I am at a loss at how to go about fixing this
When you want to generate an error in Python, you don't return a value: you raise an error.
In this case, the most appropriate would be a ZeroDivisionError:
def __init__(self, numerator = 0, denominator = 1):
self.numerator = numerator
self.denominator = denominator
if denominator == 0:
raise ZeroDivisionError('Denominator cannot be Zero!')
ZeroDivisionError is itself an specialization of "ArithmeticError" - but you could further custmize the error for your purposes by sub-classing it:
class ZeroDenominatorError(ZeroDivisionError):
pass
The main advantage of it is that the program flow can fall through layers
of code that don't handle the error properly with a try
...except
block - up to the place where you catch it - otherwise you have to put an if
statement in all places you instantiate your class to check for the type of return value.
So - that is the correct way to handle it. Now, depending on design, it may be desirable to have the instantiation of a class to fail and return another value ("None" would be better than an error message - to have an error, just raise an exception). In that case, you should write the __new__
method instead of __init__
. Unlike __init__
, it has to return the newly created instance. (And it also has to create that new instance by properly calling the __new__
method of the superclass):
from fractions import Fraction
class MixedFraction(Fraction):
def __new__(cls, numerator=0, denominator=1):
if not denominator:
return None
instance = super().__new__(cls, numerator, denominator)
return instance
def mixed(self):
quotient = self.numerator // self.denominator
remain = self.numerator - (self.denominator * quotient)
return (quotient, remain, self.denominator)
As for Hilliad's suggestion in his answer, this inherits from "Fraction" directly - meaning it can be used wherever Fraction is used, but it has the extra "mixed" method. Notice that inheriting from Fraction would require you to write __new__
instead of __init__
if you were manipulating the numerator and denominator parameters anyway: they are set in the object at the time of instantiation, in native (C
) code, and can't be further changed.