# Elegant ways to support equivalence (“equality”) in Python classes

## The Question :

440 people think this question is useful

When writing custom classes it is often important to allow equivalence by means of the == and != operators. In Python, this is made possible by implementing the __eq__ and __ne__ special methods, respectively. The easiest way I’ve found to do this is the following method:

class Foo:
def __init__(self, item):
self.item = item

def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False

def __ne__(self, other):
return not self.__eq__(other)



Do you know of more elegant means of doing this? Do you know of any particular disadvantages to using the above method of comparing __dict__s?

Note: A bit of clarification–when __eq__ and __ne__ are undefined, you’ll find this behavior:

>>> a = Foo(1)
>>> b = Foo(1)
>>> a is b
False
>>> a == b
False



That is, a == b evaluates to False because it really runs a is b, a test of identity (i.e., “Is a the same object as b?”).

When __eq__ and __ne__ are defined, you’ll find this behavior (which is the one we’re after):

>>> a = Foo(1)
>>> b = Foo(1)
>>> a is b
False
>>> a == b
True


• +1, because I didn’t know that dict used memberwise equality for ==, I had assumed it only counted them equal for same object dicts. I guess this is obvious since Python has the is operator to distinguish object identity from value comparison.
• I think the accepted answer be corrected or reassigned to Algorias’ answer, so that the strict type check is implemented.
• Also make sure hash is overridden stackoverflow.com/questions/1608842/…

356 people think this answer is useful

Consider this simple problem:

class Number:

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

n1 = Number(1)
n2 = Number(1)

n1 == n2 # False -- oops



So, Python by default uses the object identifiers for comparison operations:

id(n1) # 140400634555856
id(n2) # 140400634555920



Overriding the __eq__ function seems to solve the problem:

def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return False

n1 == n2 # True
n1 != n2 # True in Python 2 -- oops, False in Python 3



In Python 2, always remember to override the __ne__ function as well, as the documentation states:

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.

def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
return not self.__eq__(other)

n1 == n2 # True
n1 != n2 # False



In Python 3, this is no longer necessary, as the documentation states:

By default, __ne__() delegates to __eq__() and inverts the result unless it is NotImplemented. There are no other implied relationships among the comparison operators, for example, the truth of (x<y or x==y) does not imply x<=y.

But that does not solve all our problems. Let’s add a subclass:

class SubNumber(Number):
pass

n3 = SubNumber(1)

n1 == n3 # False for classic-style classes -- oops, True for new-style classes
n3 == n1 # True
n1 != n3 # True for classic-style classes -- oops, False for new-style classes
n3 != n1 # False



Note: Python 2 has two kinds of classes:

• classic-style (or old-style) classes, that do not inherit from object and that are declared as class A:, class A(): or class A(B): where B is a classic-style class;

• new-style classes, that do inherit from object and that are declared as class A(object) or class A(B): where B is a new-style class. Python 3 has only new-style classes that are declared as class A:, class A(object): or class A(B):.

For classic-style classes, a comparison operation always calls the method of the first operand, while for new-style classes, it always calls the method of the subclass operand, regardless of the order of the operands.

So here, if Number is a classic-style class:

• n1 == n3 calls n1.__eq__;
• n3 == n1 calls n3.__eq__;
• n1 != n3 calls n1.__ne__;
• n3 != n1 calls n3.__ne__.

And if Number is a new-style class:

• both n1 == n3 and n3 == n1 call n3.__eq__;
• both n1 != n3 and n3 != n1 call n3.__ne__.

To fix the non-commutativity issue of the == and != operators for Python 2 classic-style classes, the __eq__ and __ne__ methods should return the NotImplemented value when an operand type is not supported. The documentation defines the NotImplemented value as:

Numeric methods and rich comparison methods may return this value if they do not implement the operation for the operands provided. (The interpreter will then try the reflected operation, or some other fallback, depending on the operator.) Its truth value is true.

In this case the operator delegates the comparison operation to the reflected method of the other operand. The documentation defines reflected methods as:

There are no swapped-argument versions of these methods (to be used when the left argument does not support the operation but the right argument does); rather, __lt__() and __gt__() are each other’s reflection, __le__() and __ge__() are each other’s reflection, and __eq__() and __ne__() are their own reflection.

The result looks like this:

def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return NotImplemented

def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
x = self.__eq__(other)
if x is NotImplemented:
return NotImplemented
return not x



Returning the NotImplemented value instead of False is the right thing to do even for new-style classes if commutativity of the == and != operators is desired when the operands are of unrelated types (no inheritance).

Are we there yet? Not quite. How many unique numbers do we have?

len(set([n1, n2, n3])) # 3 -- oops



Sets use the hashes of objects, and by default Python returns the hash of the identifier of the object. Let’s try to override it:

def __hash__(self):
"""Overrides the default implementation"""
return hash(tuple(sorted(self.__dict__.items())))

len(set([n1, n2, n3])) # 1



The end result looks like this (I added some assertions at the end for validation):

class Number:

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

def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return NotImplemented

def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
x = self.__eq__(other)
if x is not NotImplemented:
return not x
return NotImplemented

def __hash__(self):
"""Overrides the default implementation"""
return hash(tuple(sorted(self.__dict__.items())))

class SubNumber(Number):
pass

n1 = Number(1)
n2 = Number(1)
n3 = SubNumber(1)
n4 = SubNumber(4)

assert n1 == n2
assert n2 == n1
assert not n1 != n2
assert not n2 != n1

assert n1 == n3
assert n3 == n1
assert not n1 != n3
assert not n3 != n1

assert not n1 == n4
assert not n4 == n1
assert n1 != n4
assert n4 != n1

assert len(set([n1, n2, n3, ])) == 1
assert len(set([n1, n2, n3, n4])) == 2



210 people think this answer is useful

You need to be careful with inheritance:

>>> class Foo:
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False

>>> class Bar(Foo):pass

>>> b = Bar()
>>> f = Foo()
>>> f == b
True
>>> b == f
False



Check types more strictly, like this:

def __eq__(self, other):
if type(other) is type(self):
return self.__dict__ == other.__dict__
return False



Besides that, your approach will work fine, that’s what special methods are there for.

162 people think this answer is useful

The way you describe is the way I’ve always done it. Since it’s totally generic, you can always break that functionality out into a mixin class and inherit it in classes where you want that functionality.

class CommonEqualityMixin(object):

def __eq__(self, other):
return (isinstance(other, self.__class__)
and self.__dict__ == other.__dict__)

def __ne__(self, other):
return not self.__eq__(other)

class Foo(CommonEqualityMixin):

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



15 people think this answer is useful

Not a direct answer but seemed relevant enough to be tacked on as it saves a bit of verbose tedium on occasion. Cut straight from the docs…

functools.total_ordering(cls)

Given a class defining one or more rich comparison ordering methods, this class decorator supplies the rest. This simplifies the effort involved in specifying all of the possible rich comparison operations:

The class must define one of __lt__(), __le__(), __gt__(), or __ge__(). In addition, the class should supply an __eq__() method.

New in version 2.7

@total_ordering
class Student:
def __eq__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) ==
(other.lastname.lower(), other.firstname.lower()))
def __lt__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) <
(other.lastname.lower(), other.firstname.lower()))



9 people think this answer is useful

You don’t have to override both __eq__ and __ne__ you can override only __cmp__ but this will make an implication on the result of ==, !==, < , > and so on.

is tests for object identity. This means a is b will be True in the case when a and b both hold the reference to the same object. In python you always hold a reference to an object in a variable not the actual object, so essentially for a is b to be true the objects in them should be located in the same memory location. How and most importantly why would you go about overriding this behaviour?

Edit: I didn’t know __cmp__ was removed from python 3 so avoid it.

6 people think this answer is useful

From this answer: https://stackoverflow.com/a/30676267/541136 I have demonstrated that, while it’s correct to define __ne__ in terms __eq__ – instead of

def __ne__(self, other):
return not self.__eq__(other)



you should use:

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



4 people think this answer is useful

I think that the two terms you’re looking for are equality (==) and identity (is). For example:

>>> a = [1,2,3]
>>> b = [1,2,3]
>>> a == b
True       <-- a and b have values which are equal
>>> a is b
False      <-- a and b are not the same list object



2 people think this answer is useful

The ‘is’ test will test for identity using the builtin ‘id()’ function which essentially returns the memory address of the object and therefore isn’t overloadable.

However in the case of testing the equality of a class you probably want to be a little bit more strict about your tests and only compare the data attributes in your class:

import types

class ComparesNicely(object):

def __eq__(self, other):
for key, value in self.__dict__.iteritems():
if (isinstance(value, types.FunctionType) or
key.startswith("__")):
continue

if key not in other.__dict__:
return False

if other.__dict__[key] != value:
return False

return True



This code will only compare non function data members of your class as well as skipping anything private which is generally what you want. In the case of Plain Old Python Objects I have a base class which implements __init__, __str__, __repr__ and __eq__ so my POPO objects don’t carry the burden of all that extra (and in most cases identical) logic.

2 people think this answer is useful

Instead of using subclassing/mixins, I like to use a generic class decorator

def comparable(cls):
""" Class decorator providing generic comparison functionality """

def __eq__(self, other):
return isinstance(other, self.__class__) and self.__dict__ == other.__dict__

def __ne__(self, other):
return not self.__eq__(other)

cls.__eq__ = __eq__
cls.__ne__ = __ne__
return cls



Usage:

@comparable
class Number(object):
def __init__(self, x):
self.x = x

a = Number(1)
b = Number(1)
assert a == b



1 people think this answer is useful

This incorporates the comments on Algorias’ answer, and compares objects by a single attribute because I don’t care about the whole dict. hasattr(other, "id") must be true, but I know it is because I set it in the constructor.

def __eq__(self, other):
if other is self:
return True

if type(other) is not type(self):
# delegate to superclass
return NotImplemented

return other.id == self.id