[Tutor] Overriding equality tests in Python

Steven D'Aprano steve at pearwood.info
Sat Mar 23 09:34:48 CET 2013


On 23/03/13 15:08, Robert Sjoblom wrote:
> Hi list. I'll preface this by saying that I am very grateful for all
> of you, and thank you in advance to anyone that answers.
>
> I'm currently working on a roulette simulator, because it seemed like
> fun. I found out I needed a way to compare two different outcomes, and
> it was suggested to me that I should override the __eq__ and __ne__
> methods. Said and done, I did:

> class Outcome():
>    def __init__(self, name): #Ignoring odds for now
>      self.name = name
>
>    def __eq__(self, other):
>      '''returns True if Outcome.name matches other.name''':
>      if self.name == other.name: return True
>    def __ne__(self, other):
>      '''returns True if Outcome.name does not match other.name'''
>      if self.name != other.name: return True


I'm not sure what relevance a name has for roulette simulator, but okay.

Those two methods are probably better written something like this:


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

     def __ne__(self, other):
         return (self.name != other.name)


Actually, here's an even better way of doing it:


     def __eq__(self, other):
         if isinstance(other, Outcome):
             return (self.name == other.name)
         # Give the other object a chance to check for equality.
         return NotImplemented

     def __ne__(self, other):
         return not self == other



> Now, this works, as far as this is concerned:
>>>> a = Outcome('Bob')
>>>> b = Outcome('Ray')
>>>> c = Outcome('Bob')
>>>> a == b
>>>> a == c
> True
>>>> a != b
> True


When testing, you should also test values that give the opposite result. For example, compare a == b and a != c.


> However, if I were to create a class without the __eq__ and __ne__
> definitions, what is to prevent me from doing: a.name == b.name ?

Nothing. But consider a more complicated example. Which would you rather write?


     a.name.lower() == b.name.lower() and a.age == b.age and a.sex == b.sex

     a == b


The idea of defining __eq__ is that you can hide all the complexity of checking equality in a single method. For example, the __eq__ for dicts might look something like this:


     def __eq__(self, other):
         if self is other:
             return True
         if not isinstance(self, other):
             return NotImplemented
         if len(self) != len(other):
             return False
         for key in self:
             if key not in other:
                 return False
             if self[key] != self[other]:
                 return False
         return True


Imagine having to write something like that every single time you wanted to compare to dicts for equality.


> Or
> am I missing something in my implementation of the overrides? Is there
> a reason why I shouldn't do .name comparisons?

As I said, I don't really understand why a roulette outcome has a name in the first place, but given that it does, I don't any problem with comparing the names directly. Still, I would probably write it as an __eq__ method, since it's easier to write a == b than a.name == b.name.



-- 
Steven


More information about the Tutor mailing list