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