Dimitris Fasarakis Hilliard
Dimitris Fasarakis Hilliard

Reputation: 160377

No implied relationship among the comparison operators

I recently started reading The Python Language Reference, Release 2.7.10.

In Section 3.4: Special Method Names and particularly regarding the comparison operators object.__eq__(self, other) and object.__ne__(self, other) it states the following which has lead to some confusion:

There are no implied relationships among the comparison operators. The truth of x==y does not imply that x!=y is false. Accordingly, when defining eq(), one should also define ne() so that the operators will behave as expected.


What exactly does this statement mean? How can the truth of x==y not automatically and without question translate to a false value of x!=y?

Upvotes: 1

Views: 83

Answers (2)

chepner
chepner

Reputation: 530843

Here's a contrived example that demonstrates one possible use of decoupling the two.

class Z7(object):

    def __init__(self, x):
        self.x = x

    def __eq__(self, other):
        return self.x == other.x

    def __ne__(self, other):
        return self.x % 7 != other.x % 7

This lets us make a three-way distinction between two objects: numbers that are exactly equal (== returns True), numbers that are equal modulo 7 (== and != return False), and numbers that are unequal modulo 7 (!= returns True).

if x == y:
    # E.g. Z7(3) == Z7(3)
    print("x and y are identical)"
elif x != y:
    # E.g. Z7(3) != Z7(5)
    print("x and y are unalike")
else:
    # Neither are true, so thy must be similar
    # E.g. Z7(3) and Z7(10)
    print("x and y are similar")

This doesn't use the 4th possibility, where == and != both return True, which I cannot think of a good use for.

Upvotes: 1

Kevin
Kevin

Reputation: 76184

"no implied relationships" means that when you use "!=", if you haven't implemented __ne__, it's not going to instead call __eq__ and negate the result. It will use the __ne__ method inherited from its parent. Most of the time, this will resolve to object.__ne__ which only checks for referential equality.

>>> class Fred:
...     def __eq__(self, other):
...         print "eq was called!"
...         return False
...
>>> x = Fred()
>>> print x == 23
eq was called!
False
>>> #if eq and ne had an implied relationship, 
>>> #we'd expect this next line to also print "eq was called!"
>>> print x != 23
True
>>> #... but it doesn't.

It also means that you're free to define __eq__ and __ne__ in ways that seem mathematically contradictory. Python won't hold your hand.

>>> class Fred:
...     def __eq__(self, other):
...         return True
...     def __ne__(self, other):
...         return True
...
>>> x = Fred()
>>> print x == 23
True
>>> print x != 23
True

Although it does suggest that you should implement them in a mathematically sensible way. The above code block is legal, but not wise.

Upvotes: 3

Related Questions