class A:
def __lt__(self, o):
return "hello"
a = A()
b = A()
print(a < b) # hello
print(a < a) # hello
a = (A(),)
b = (A(),)
print(a < b) # hello
print(a < a) # False
I have no idea why the last one is False. It looks like it could be a bug due to optimization (maybe?). Could anyone explain why this is happening?
a < a is False because tuples form a strict total order, which requires a tuple not be less than itself. That means we don’t need to compare a[0] to a[0] to determine the answer; it’s false for any given tuple a. Only when we compare two different tuples do we need to compare corresponding elements in lexicographical comparison.
Since A doesn’t define __eq__, it defaults to object.__eq__, which compares object IDs to check for identity.
This has a significant impact on lexicographic comparison in tuples. The IDs of a[0] and a[0] are the same, but a[0] and b[0] have different IDs. As a result, a < a defaults to comparing the tuple lengths, which ultimately yields False.
Thanks for your response…! It looks like our answers overlapped since we replied at the same time. Your explanation also makes a lot of sense from a broader perspective. Thank you again for taking the time to respond!
And to add to this, suppose that the object defines equality in some way - the same result would be seen any time the tuples’ corresponding elements compare equal. To get __lt__ to be called, you need to define __eq__ to return False.
I think it’s not so much about object identifiers, but tuplerichcompare does go straight to lexicographical comparison. However, it only cares about the boolean result, and "hello" is a truthy value, so it exhausts all comparisons, and so falls back on the lengths as you point out.
I thought there would be a short path that tests for and handles object identity, but apparently not.