[Python-ideas] Automatic total ordering
Jason Orendorff
jason.orendorff at gmail.com
Thu Oct 16 21:02:12 CEST 2008
Terry Reedy wrote:
> In 3.0, the actual definitions are the C equivalent of
>
> class object():
> def __eq__(self,other): return id(self) == id(other)
> def __ne__(self,other): return id(self) != id(other)
> def __lt__(self,other): return NotImplemented
Not exactly. For example, object().__eq__(object()) ==>
NotImplemented, not False; and __ne__ calls __eq__, at least
sometimes. The best I can do is:
def __eq__(self, other):
if self is other:
return True
return NotImplemented
def __ne__(self, other):
# calls PyObject_RichCompare(self, other, Py_EQ)...
eq = (self == other) # ...which is kinda like this
if eq is NotImplemented: # is this even possible?
return NotImplemented
return not eq
def __lt__(self, other):
return NotImplemented
This behavior makes sense to me, except for __ne__ calling
PyObject_RichCompare, which seems like a bug. If I understand
correctly, object.__ne__ should be more like this:
def __ne__(self, other):
eq = self.__eq__(other)
if eq is NotImplemented: return NotImplemented
return not eq
(When I write something like `self.__eq__(other)`, here and below,
what I really mean is something more like what half_richcompare does,
avoiding the instance dict and returning NotImplemented if the method
is not found.)
The current behavior causes __eq__ to be called four times in cases
where two seems like enough. Rather confusing.
So I think the proposal is to change the other three methods to try
using __lt__ and __eq__ in a similar way:
def __le__(self, other):
# Note: NotImplemented is truthy, so if either of these
# returns NotImplemented, __le__ returns NotImplemented.
return self.__lt__(other) or self.__eq__(other)
def __gt__(self, other):
# 'and' isn't quite as convenient for us here as 'or'
# was above, so spell out what we want:
lt = self.__lt__(other)
if lt is NotImplemented: return NotImplemented
if lt: return False
eq = self.__eq__(other)
if eq is NotImplemented: return NotImplemented
return not eq
def __ge__(self, other):
lt = self.__lt__(other)
if lt is NotImplemented: return NotImplemented
return not lt
These methods never call __eq__ without first calling __lt__. That's
significant: if __lt__ always returns NotImplemented--the
default--then these methods should always return NotImplemented too:
we don't want to get a bogus True or False result based on a
successful call to __eq__.
It would also be nice to stop telling people that:
x.__eq__(y) <==> x==y
x.__ne__(y) <==> x!=y
and so forth, in the docstrings and the language reference, as that's
an awfully loose approximation of the truth.
-j
More information about the Python-ideas
mailing list