Home > Enterprise >  Overriding __eq__ and comparing two objects where one of the types is classname.classname
Overriding __eq__ and comparing two objects where one of the types is classname.classname

Time:12-05

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

  • Related