[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