For user-defined class instances, is
is used as a fallback - where the default __eq__
isn't overridden, a == b
is evaluated as a is b
. This ensures that the comparison will always have a result (except in the NotImplemented
case, where comparison is explicitly forbidden).
This is (somewhat obliquely - good spot Sven Marnach) referred to in the data model documentation (emphasis mine):
User-defined classes have __eq__()
and __hash__()
methods by
default; with them, all objects compare unequal (except with
themselves) and x.__hash__()
returns an appropriate value such
that x == y
implies both that x is y
and hash(x) == hash(y)
.
You can demonstrate it as follows:
>>> class Unequal(object):
def __eq__(self, other):
return False
>>> ue = Unequal()
>>> ue is ue
True
>>> ue == ue
False
so __eq__
must be called before id
, but:
>>> class NoEqual(object):
pass
>>> ne = NoEqual()
>>> ne is ne
True
>>> ne == ne
True
so id
must be invoked where __eq__
isn't defined.
You can see this in the CPython implementation, which notes:
/* If neither object implements it, provide a sensible default
for == and !=, but raise an exception for ordering. */
The "sensible default" implemented is a C-level equality comparison of the pointers v
and w
, which will return whether or not they point to the same object.