I have a class Rational.
class Rational:
def __init__(self, numerator, denominator=1):
self.numerator = numerator
self.denominator = denominator
r1 = Rational(1, 4)
r3 = Rational(r1)
I want the following to print True
print(r3 == r1)
I am struggling with the concept of Rational(r1). This would give me the type Rational.Rational. In trying to override __eq__, I've tried writing:
def __eq__(other, x: Rational.Rational)
but this doesn't work. How do i make r3 copy the values of r1?
I tried changing init into
@dispatch
def __init__(self, x):
self.numerator = x.numerator
self.denominator = x.denominator
but this gives me "TypeError: _() takes 1 positional argument but 3 were given".
And this is my current definition of eq:
def __eq__(self, other):
x = False
if self.numerator == other.numerator and self.denominator == other.denominator:
print(f"Self.numerator: {self.numerator} and Self.denominator: {self.denominator}")
x = True
return x
CodePudding user response:
Rather than try to overload Rational.__init__ to accept two int values or a single Rational, define a class method that breaks down a Rational value into its int components to create a new Rational.
class Rational:
def __init__(self, numerator, denominator):
self.numerator = numerator
self.denominator = denominator
@classmethod
def from_rational(cls, r: 'Rational'):
return cls(r.numerator, r.denominator)
def __eq__(self, other):
return (self.numerator == other.numerator
and self.denominator == other.denominator)
r1 = Rational(1, 4)
r3 = Rational.from_rational(r1)
You'll note that with this definition, Rational(1,2) == Rational(2,4) will return False. I leave it as an exercise to modify Rational.__init__ to modify which values you actually store to make __eq__ work. (Rather than modifying __eq__, you'll want to store a "canonical" representation of the rational rather than simply storing the arguments.)
This definition also assumes duck typing: you can successfully compare any value with numerator and denominator attributes to a Rational. You may want to make the definition more robust (what if other doesn't have both attributes), as well as allow comparisons like Rational(6,2) == 3 to return True.
CodePudding user response:
from functools import singledispatchmethod
class Rational:
@singledispatchmethod
def __init__(self, numerator):
self.numerator = numerator.numerator
self.denominator = numerator.denominator
@__init__.register
def _(self, numerator:int, denominator=1):
self.numerator = numerator
self.denominator = denominator
def __repr__(self):
return f'Rational({self.numerator}, {self.denominator})'
def __eq__(self, other):
return self.numerator == other.numerator and self.denominator == other.denominator
r1 = Rational(1, 4)
r3 = Rational(r1)
print(r1)
print(r3)
print(r1 == r3)
You can use functools.singledispatchmethod on __init__ (note, requires python3.8 ). Because Rational is not yet defined, your default implementation would be for the case when first (and only) non-self argument is instance of class Rational, i.e. it has numerator and denominator attributes. Then you can register another implementation for int. Now I leave it to you to implement it for str, e.g. '1/4'.
As per your definition, two instances of Rational are equal only if they hold the exact same numbers, i.e. Rational(1, 4) and Rational(2, 8) are not considered equal, despite 1/4 == 2/8.
Finally, let mention there is fractions module from Standard LIbrary
