This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Sorting falls back to use __gt__ when __lt__ is not present
Type: Stage:
Components: Documentation Versions: Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: docs@python Nosy List: docs@python, mark.dickinson, rhettinger, steven.daprano, tim.peters, yanmitrofanov
Priority: normal Keywords:

Created on 2020-01-04 15:29 by yanmitrofanov, last changed 2022-04-11 14:59 by admin.

Messages (7)
msg359293 - (view) Author: Yan Mitrofanov (yanmitrofanov) Date: 2020-01-04 15:29
Sorting documentation claims that sorting algorithm is only using < comparisons
https://docs.python.org/3/howto/sorting.html#odd-and-ends
https://docs.python.org/3/library/stdtypes.html#list.sort

When __lt__ implementation is missing, you get an exception
class Foo:
    pass
sorted([Foo(), Foo(), Foo()])
TypeError: '<' not supported between instances of 'Foo' and 'Foo'

However, if implement __gt__ method, you doesn't get an exception
class Foo:
    def __gt__(self, other):
        return False
sorted([Foo(), Foo(), Foo()])  # ok


Is it supposed to work like this? Or is it lack of documentation?
msg359294 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2020-01-04 16:06
The `<` comparison uses `__lt__` dunder if it exists, otherwise it falls back on `__gt__`.
msg359295 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2020-01-04 16:19
To be precise, when doing `a < b`, either `a.__lt__` or `b.__gt__` can be used, since `__gt__` is considered the reversed / reflected version of `__lt__` (analogous to `__add__` and `__radd__`).


>>> class A:
...     def __lt__(self, other): return False
... 
>>> class B:
...     def __gt__(self, other): return True
... 
>>> A() < B()
False
>>> B() < A()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'B' and 'A'
>>> sorted([A(), B()])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'B' and 'A'
>>> sorted([B(), A()])
[<__main__.B object at 0x10dc4cca0>, <__main__.A object at 0x10dc68ca0>]

Presumably in the normal case, all the objects being sorted have the same type, and so in that case it's enough that the type implements at least one of __lt__ and __gt__.
msg359296 - (view) Author: Yan Mitrofanov (yanmitrofanov) Date: 2020-01-04 17:30
I got your point. So it seems that two pieces of documentation are not equivalent: 

https://docs.python.org/3/howto/sorting.html#odd-and-ends
> The sort routines are guaranteed to use __lt__() when making comparisons between two objects.

https://docs.python.org/3/library/stdtypes.html#list.sort
> This method sorts the list in place, using only < comparisons between items.
msg359298 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2020-01-04 18:23
Right, the HOWTO language could possibly be improved. The intended and practical message is that you *only* need to provide __lt__ everywhere for sort to work.
msg359320 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2020-01-05 02:19
It's hard to be clearer without being annoyingly wordy.  The truth is that sort does all comparisons by calling the CAPI:

    PyObject_RichCompareBool(v, w, Py_LT)`

Sorting doesn't know (or care) how `PyObject_RichCompareBool()` is implemented.  The closest Python equivalent is:

    bool(v < w)

although then you also have to be clear that `bool` refers to the builtin function of that name.

Regardless, the sorting docs certainly aren't the place to explain how `v < w` is implemented.  For example, that it _may_ end up calling `w.__gt__(v)` has nothing to do with sorting - instead that's about the semantics of comparison operators, regardless of context.

Since, best I can recall, nobody has asked about this before, perhaps the docs don't need "improvement" ;-)  If they do, then I'm with Mark:  the _intent_ was to say "if you want a class to implement its own sorting order, then it's sufficient to implement `__lt__` alone, and then sorting will use only that comparison operator if the list contains only instances of that class".
msg359322 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2020-01-05 04:37
On Sun, Jan 05, 2020 at 02:19:49AM +0000, Tim Peters wrote:

> Since, best I can recall, nobody has asked about this before

See discussion here:

https://mail.python.org/archives/list/python-dev@python.org/message/5AQMG6ADD6RGPLI3VTILB2MKXMBFTIGU/

which followed from discussions on sort order. I was asked to provide a 
PR, but I have technical difficulties with github.

I think it's sufficient to say that sorting relies only on the `<` 
operator, and link to the section of the docs that map special dunders 
to comparison operators, namely here:

https://docs.python.org/3/reference/datamodel.html#object.__lt__

where it goes into detail about reflected methods and such.
History
Date User Action Args
2022-04-11 14:59:24adminsetgithub: 83391
2020-01-05 04:37:37steven.dapranosetmessages: + msg359322
2020-01-05 02:19:49tim.peterssetmessages: + msg359320
2020-01-04 18:23:29mark.dickinsonsetnosy: + tim.peters, rhettinger
messages: + msg359298
2020-01-04 17:30:22yanmitrofanovsetmessages: + msg359296
2020-01-04 16:19:52mark.dickinsonsetnosy: + mark.dickinson
messages: + msg359295
2020-01-04 16:06:54steven.dapranosetnosy: + steven.daprano
messages: + msg359294
2020-01-04 15:29:55yanmitrofanovcreate