Python, should I implement __ne__()
operator based on __eq__
?
Short Answer: Don't implement it, but if you must, use ==
, not __eq__
In Python 3, !=
is the negation of ==
by default, so you are not even required to write a __ne__
, and the documentation is no longer opinionated on writing one.
Generally speaking, for Python 3-only code, don't write one unless you need to overshadow the parent implementation, e.g. for a builtin object.
That is, keep in mind Raymond Hettinger's comment:
The __ne__
method follows automatically from __eq__
only if
__ne__
isn't already defined in a superclass. So, if you're
inheriting from a builtin, it's best to override both.
If you need your code to work in Python 2, follow the recommendation for Python 2 and it will work in Python 3 just fine.
In Python 2, Python itself does not automatically implement any operation in terms of another - therefore, you should define the __ne__
in terms of ==
instead of the __eq__
.
E.G.
class A(object):
def __eq__(self, other):
return self.value == other.value
def __ne__(self, other):
return not self == other # NOT `return not self.__eq__(other)`
See proof that
- implementing
__ne__()
operator based on __eq__
and
- not implementing
__ne__
in Python 2 at all
provides incorrect behavior in the demonstration below.
Long Answer
The documentation for Python 2 says:
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.
So that means that if we define __ne__
in terms of the inverse of __eq__
, we can get consistent behavior.
This section of the documentation has been updated for Python 3:
By default, __ne__()
delegates to __eq__()
and inverts the result
unless it is NotImplemented
.
and in the "what's new" section, we see this behavior has changed:
!=
now returns the opposite of ==
, unless ==
returns NotImplemented
.
For implementing __ne__
, we prefer to use the ==
operator instead of using the __eq__
method directly so that if self.__eq__(other)
of a subclass returns NotImplemented
for the type checked, Python will appropriately check other.__eq__(self)
From the documentation:
The NotImplemented
object
This type has a single value. There is a single object with this value. This object is accessed through the built-in name
NotImplemented
. 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.
When given a rich comparison operator, if they're not the same type, Python checks if the other
is a subtype, and if it has that operator defined, it uses the other
's method first (inverse for <
, <=
, >=
and >
). If NotImplemented
is returned, then it uses the opposite's method. (It does not check for the same method twice.) Using the ==
operator allows for this logic to take place.
Expectations
Semantically, you should implement __ne__
in terms of the check for equality because users of your class will expect the following functions to be equivalent for all instances of A.:
def negation_of_equals(inst1, inst2):
"""always should return same as not_equals(inst1, inst2)"""
return not inst1 == inst2
def not_equals(inst1, inst2):
"""always should return same as negation_of_equals(inst1, inst2)"""
return inst1 != inst2
That is, both of the above functions should always return the same result. But this is dependent on the programmer.
Demonstration of unexpected behavior when defining __ne__
based on __eq__
:
First the setup:
class BaseEquatable(object):
def __init__(self, x):
self.x = x
def __eq__(self, other):
return isinstance(other, BaseEquatable) and self.x == other.x
class ComparableWrong(BaseEquatable):
def __ne__(self, other):
return not self.__eq__(other)
class ComparableRight(BaseEquatable):
def __ne__(self, other):
return not self == other
class EqMixin(object):
def __eq__(self, other):
"""override Base __eq__ & bounce to other for __eq__, e.g.
if issubclass(type(self), type(other)): # True in this example
"""
return NotImplemented
class ChildComparableWrong(EqMixin, ComparableWrong):
"""__ne__ the wrong way (__eq__ directly)"""
class ChildComparableRight(EqMixin, ComparableRight):
"""__ne__ the right way (uses ==)"""
class ChildComparablePy3(EqMixin, BaseEquatable):
"""No __ne__, only right in Python 3."""
Instantiate non-equivalent instances:
right1, right2 = ComparableRight(1), ChildComparableRight(2)
wrong1, wrong2 = ComparableWrong(1), ChildComparableWrong(2)
right_py3_1, right_py3_2 = BaseEquatable(1), ChildComparablePy3(2)
Expected Behavior:
(Note: while every second assertion of each of the below is equivalent and therefore logically redundant to the one before it, I'm including them to demonstrate that order does not matter when one is a subclass of the other.)
These instances have __ne__
implemented with ==
:
assert not right1 == right2
assert not right2 == right1
assert right1 != right2
assert right2 != right1
These instances, testing under Python 3, also work correctly:
assert not right_py3_1 == right_py3_2
assert not right_py3_2 == right_py3_1
assert right_py3_1 != right_py3_2
assert right_py3_2 != right_py3_1
And recall that these have __ne__
implemented with __eq__
- while this is the expected behavior, the implementation is incorrect:
assert not wrong1 == wrong2 # These are contradicted by the
assert not wrong2 == wrong1 # below unexpected behavior!
Unexpected Behavior:
Note that this comparison contradicts the comparisons above (not wrong1 == wrong2
).
>>> assert wrong1 != wrong2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
and,
>>> assert wrong2 != wrong1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
Don't skip __ne__
in Python 2
For evidence that you should not skip implementing __ne__
in Python 2, see these equivalent objects:
>>> right_py3_1, right_py3_1child = BaseEquatable(1), ChildComparablePy3(1)
>>> right_py3_1 != right_py3_1child # as evaluated in Python 2!
True
The above result should be False
!
Python 3 source
The default CPython implementation for __ne__
is in typeobject.c
in object_richcompare
:
case Py_NE:
/* By default, __ne__() delegates to __eq__() and inverts the result,
unless the latter returns NotImplemented. */
if (Py_TYPE(self)->tp_richcompare == NULL) {
res = Py_NotImplemented;
Py_INCREF(res);
break;
}
res = (*Py_TYPE(self)->tp_richcompare)(self, other, Py_EQ);
if (res != NULL && res != Py_NotImplemented) {
int ok = PyObject_IsTrue(res);
Py_DECREF(res);
if (ok < 0)
res = NULL;
else {
if (ok)
res = Py_False;
else
res = Py_True;
Py_INCREF(res);
}
}
break;
But the default __ne__
uses __eq__
?
Python 3's default __ne__
implementation detail at the C level uses __eq__
because the higher level ==
(PyObject_RichCompare) would be less efficient - and therefore it must also handle NotImplemented
.
If __eq__
is correctly implemented, then the negation of ==
is also correct - and it allows us to avoid low level implementation details in our __ne__
.
Using ==
allows us to keep our low level logic in one place, and avoid addressing NotImplemented
in __ne__
.
One might incorrectly assume that ==
may return NotImplemented
.
It actually uses the same logic as the default implementation of __eq__
, which checks for identity (see do_richcompare and our evidence below)
class Foo:
def __ne__(self, other):
return NotImplemented
__eq__ = __ne__
f = Foo()
f2 = Foo()
And the comparisons:
>>> f == f
True
>>> f != f
False
>>> f2 == f
False
>>> f2 != f
True
Performance
Don't take my word for it, let's see what's more performant:
class CLevel:
"Use default logic programmed in C"
class HighLevelPython:
def __ne__(self, other):
return not self == other
class LowLevelPython:
def __ne__(self, other):
equal = self.__eq__(other)
if equal is NotImplemented:
return NotImplemented
return not equal
def c_level():
cl = CLevel()
return lambda: cl != cl
def high_level_python():
hlp = HighLe