Gaurav Sachdeva
Gaurav Sachdeva

Reputation: 672

__eq__ method returns True for both == and > operators

I have declared a class Triangle in Python which takes base and height as __init__ arguments and has a method area that computes and returns the area of the triangle. The __eq__ method of the Triangle class compares the area of the triangles and return the value. The class is defined as below:

class Shape(object):
    def area(self):
        raise AttributeException("Subclasses should override this method.")

class Triangle(Shape):
    def __init__(self, base, height):
        """
        base = base of the triangle.
        height = height of the triangle
        """
        self.base = base
        self.height = height
    def area(self):
        return (self.base * self.height)/2
    def __str__(self):
        return 'Triangle with base ' + str(self.base) + ' and height ' + str(self.height)
    def __eq__(self, other):
        """
        Two triangles are equal if their area is equal
        """
        return (self.area() == other.area())

Now I ran the program and created two instances to Triangle t1 and t3, and gave them different base and height but their area is equal. So t1 == t3 should be True which is returned as True only. But strangely, t1 > t3 is also returned as True, which I don't understand why?

>>> t1 = Triangle(3,4)
>>> t3 = Triangle(2, 6)
>>> t1.area()
6
>>> t3.area()
6
>>> t1 == t3
True
>>> t1 > t3
True
>>> t1 < t3
False
>>> 

Upvotes: 5

Views: 4972

Answers (2)

Taku
Taku

Reputation: 33744

According to the docs

If no __cmp__(), __eq__() or __ne__() operation is defined, class instances are compared by object identity (“address”).

Also stated in this answer, the newer versions of python 2 default uses the object's id for comparison.

So you should always define either __gt__ or __lt__ alongside of __eq__ for comparing the way you wanted. And if you want, you can also define __le__, __ge__, __ne__...

One simple thing you can change is:

class Triangle(Shape):
    def __init__(self, base, height):
        """
        base = base of the triangle.
        height = height of the triangle
        """
        self.base = base
        self.height = height
    def area(self):
        return (self.base * self.height)/2
    def __str__(self):
        return 'Triangle with base ' + str(self.base) + ' and height ' + str(self.height)
    def __eq__(self, other):
        """
        Two triangles are equal if their area is equal
        """
        return (self.area() == other.area())
    def __gt__(self, other):
        """
        This works for both greater than and less than
        """
        return (self.area() > other.area())

Upvotes: 2

L3viathan
L3viathan

Reputation: 27333

Although I can't find a source on this, it seems as if Python 2 uses the id to compare two objects if you don't define the respective magic methods yourself.

Observe:

>>> t1 = Triangle(3, 4)
>>> t3 = Triangle(2, 6)
>>> t1 > t3
False
>>> t1 < t3
True
>>> t1 = Triangle(3, 4)
>>> t1 > t3
True
>>> t1 < t3
False

This behaviour isn't guaranteed as id doesn't promise you to assign increasing numbers to objects created later, but it does usually seem to work in CPython 2.7, since id will give you the physical address of the object.

You will observe that before the redefinition of t1, id(t1) < id(t3), while afterwards, the opposite is the case.

Upvotes: 4

Related Questions