Python allows expressions like x > y > z, which, according to the docs, is equivalent to (x > y) and (y > z) except y is only evaluated once. (https://docs.python.org/3/reference/expressions.html)
However, this seems to break if I customize comparison functions. E.g. suppose I have the following class: (Apologies for the large block, but once you read the __eq__ method, the rest is trivial.)
class CompareList(list):
def __repr__(self):
return "CompareList([" + ",".join(str(x) for x in self) + "])"
def __eq__(self, other):
if isinstance(other, list):
return CompareList(self[idx] == other[idx] for idx in xrange(len(self)))
else:
return CompareList(x == other for x in self)
def __ne__(self, other):
if isinstance(other, list):
return CompareList(self[idx] != other[idx] for idx in xrange(len(self)))
else:
return CompareList(x != other for x in self)
def __gt__(self, other):
if isinstance(other, list):
return CompareList(self[idx] > other[idx] for idx in xrange(len(self)))
else:
return CompareList(x > other for x in self)
def __ge__(self, other):
if isinstance(other, list):
return CompareList(self[idx] >= other[idx] for idx in xrange(len(self)))
else:
return CompareList(x >= other for x in self)
def __lt__(self, other):
if isinstance(other, list):
return CompareList(self[idx] < other[idx] for idx in xrange(len(self)))
else:
return CompareList(x < other for x in self)
def __le__(self, other):
if isinstance(other, list):
return CompareList(self[idx] <= other[idx] for idx in xrange(len(self)))
else:
return CompareList(x <= other for x in self)
Now I can do fun stuff like CompareList([10, 5]) > CompareList([5, 10]) and it will correctly return CompareList([True,False])
However, chaining these operations doesn't work nicely:
low = CompareList([1])
high = CompareList([2])
print(low > high > low) # returns CompareList([True])
Why not? What happens under the hood here? I know it isn't equivalent to (low > high) > low = (False > low) (because that would return False). It could be low > (high > low) but that wouldn't make sense in terms of operator precedence (normally left-to-right).
functools.total_ordering- if I recall correctly, you only need to supply one cmp eg__eq__and one ordering, eg__lt__and add@functools.total_orderingto the class, and it should fill in the rest for you.