The list.sort(reverse=True) method not consistent with description

Dear Python developers, The help(list) shows in a python console the following documentation string for the list.sort() method. sort(self, /, *, key=None, reverse=False) | Sort the list in ascending order and return None. | | The sort is in-place (i.e. the list itself is modified) and stable (i.e. the | order of two equal elements is **maintained**). Please notice the following inconsistency in Python3.10.0 and before of a sort(reverse=True) result:
L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] L.sort(reverse=True) L [(3, 'e'), (2, 'd'), (2, 'b'), (1, 'c'), (1, 'a')]
it should be:
L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] reverseTuplesSort(L) [(3, 'e'), (2, 'b'), (2, 'd'), (1, 'a'), (1, 'c')]
Same inconsistency appears when using a sorting key. Passing easily unnoticed may produce unexpected and potentially wrong ranking results. Best Regards, RB -- 50, Bvd G.-D. Charlotte L-1330 Luxembourg GD Luxembourg (Europe) (+352) 621 175 242

[Raymond Bisdorff <raymond.bisdorff@pt.lu>]
... Please notice the following inconsistency in Python3.10.0 and before of a sort(reverse=True) result:
L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] L.sort(reverse=True) L [(3, 'e'), (2, 'd'), (2, 'b'), (1, 'c'), (1, 'a')]
Looks good to me.
it should be:
L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] reverseTuplesSort(L) [(3, 'e'), (2, 'b'), (2, 'd'), (1, 'a'), (1, 'c')]
Stability is irrelevant in the example, because no two list elements are equal. You appear to be thinking, perhaps, that s[0] == t[0] when s == (1, 'a') and t == (1, 'c') means s and t "are equal", but that's not so at all.
s = (1, 'a') t = (1, 'c') s == t False s < t True t > s True
So s MUST come before t in a forward sort, and t MUST come before s in a reverse sort.

Dear All, I fully agree with your point. By default, all the components of the tuple should be used in the comparison. Yet, I was confused by the following result.
from operator import itemgetter L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] L.sort(key=itemgetter(0), reverse=True) L = [(3, 'e'), (2, 'd'), (2, 'b'), (1, 'c'), (1, 'a')]
Should the tuples comparison is in this case, I thought, not be solely based on the first tuple component? Best Regards On 10/30/21 18:26, Tim Peters wrote:
[Raymond Bisdorff <raymond.bisdorff@pt.lu>]
... Please notice the following inconsistency in Python3.10.0 and before of a sort(reverse=True) result:
L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] L.sort(reverse=True) L [(3, 'e'), (2, 'd'), (2, 'b'), (1, 'c'), (1, 'a')] Looks good to me.
it should be:
L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] reverseTuplesSort(L) [(3, 'e'), (2, 'b'), (2, 'd'), (1, 'a'), (1, 'c')] Stability is irrelevant in the example, because no two list elements are equal. You appear to be thinking, perhaps, that s[0] == t[0] when s == (1, 'a') and t == (1, 'c') means s and t "are equal", but that's not so at all.
s = (1, 'a') t = (1, 'c') s == t False s < t True t > s True
So s MUST come before t in a forward sort, and t MUST come before s in a reverse sort.
-- 50, Bvd G.-D. Charlotte L-1330 Luxembourg GD Luxembourg (Europe) (+352) 621 175 242

[Raymond Bisdorff <raymond.bisdorff@pt.lu>]
I fully agree with your point. By default, all the components of the tuple should be used in the comparison.
Yet, I was confused by the following result.
from operator import itemgetter L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] L.sort(key=itemgetter(0), reverse=True) L = [(3, 'e'), (2, 'd'), (2, 'b'), (1, 'c'), (1, 'a')]
Should the tuples comparison is in this case, I thought, not be solely based on the first tuple component?
I'm not sure what you're doing there. The last line in your example isn't showing the result of sorting, it's assigning a brand new list to `L`' Here the same thing, but changing the last line to show the result of sorting:
from operator import itemgetter L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] L.sort(key=itemgetter(0), reverse=True) L [(3, 'e'), (2, 'b'), (2, 'd'), (1, 'a'), (1, 'c')]
So stability does matter when comparing only the tuples' first components, and tuples with the same first component did retain their original order.

Dear All, You are all completely right. Sorry for the confusion. Thank you all very much for putting my mind right about this issue. Best Regards Raymond Bisdorff
On 30 Oct 2021, at 19:00, Tim Peters <tim.peters@gmail.com> wrote:
[Raymond Bisdorff <raymond.bisdorff@pt.lu>]
I fully agree with your point. By default, all the components of the tuple should be used in the comparison.
Yet, I was confused by the following result.
from operator import itemgetter L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] L.sort(key=itemgetter(0), reverse=True) L = [(3, 'e'), (2, 'd'), (2, 'b'), (1, 'c'), (1, 'a')]
Should the tuples comparison is in this case, I thought, not be solely based on the first tuple component?
I'm not sure what you're doing there. The last line in your example isn't showing the result of sorting, it's assigning a brand new list to `L`' Here the same thing, but changing the last line to show the result of sorting:
from operator import itemgetter L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] L.sort(key=itemgetter(0), reverse=True) L [(3, 'e'), (2, 'b'), (2, 'd'), (1, 'a'), (1, 'c')]
So stability does matter when comparing only the tuples' first components, and tuples with the same first component did retain their original order.

You are re-assigning the list on line 4 here, not displaying it. I get the answer you expect when using the `itemgetter(0)` key: IPython 7.28.0, on CPython 3.9.7 (default, Aug 31 2021 13:28:12)
import operator from operator import itemgetter L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] L.sort(key=operator.itemgetter(0), reverse=True) L [(3, 'e'), (2, 'b'), (2, 'd'), (1, 'a'), (1, 'c')]
On 10/30/21 12:47, Raymond Bisdorff wrote:
Dear All,
I fully agree with your point. By default, all the components of the tuple should be used in the comparison.
Yet, I was confused by the following result.
from operator import itemgetter L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] L.sort(key=itemgetter(0), reverse=True) L = [(3, 'e'), (2, 'd'), (2, 'b'), (1, 'c'), (1, 'a')]
Should the tuples comparison is in this case, I thought, not be solely based on the first tuple component? Best Regards
On 10/30/21 18:26, Tim Peters wrote:
[Raymond Bisdorff <raymond.bisdorff@pt.lu>]
... Please notice the following inconsistency in Python3.10.0 and before of a sort(reverse=True) result:
>>> L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] >>> L.sort(reverse=True) >>> L >>> [(3, 'e'), (2, 'd'), (2, 'b'), (1, 'c'), (1, 'a')] Looks good to me.
it should be:
>>> L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] >>> reverseTuplesSort(L) [(3, 'e'), (2, 'b'), (2, 'd'), (1, 'a'), (1, 'c')] Stability is irrelevant in the example, because no two list elements are equal. You appear to be thinking, perhaps, that s[0] == t[0] when s == (1, 'a') and t == (1, 'c') means s and t "are equal", but that's not so at all.
s = (1, 'a') t = (1, 'c') s == t False s < t True t > s True
So s MUST come before t in a forward sort, and t MUST come before s in a reverse sort.

On 31/10/21 5:47 am, Raymond Bisdorff wrote:
Should the tuples comparison is in this case, I thought, not be solely based on the first tuple component?
Whether you think it should or shouldn't, the fact is that it's not. This is documented in Section 5.6 of the Library Reference: "tuples and lists are compared lexicographically by comparing corresponding elements. This means that to compare equal, every element must compare equal and the two sequences must be of the same type and have the same length." -- Greg

On Sun, Oct 31, 2021 at 01:32:29PM +1300, Greg Ewing wrote:
On 31/10/21 5:47 am, Raymond Bisdorff wrote:
Should the tuples comparison is in this case, I thought, not be solely based on the first tuple component?
Whether you think it should or shouldn't, the fact is that it's not. This is documented in Section 5.6 of the Library Reference:
"tuples and lists are compared lexicographically by comparing corresponding elements. This means that to compare equal, every element must compare equal and the two sequences must be of the same type and have the same length."
I don't think that applies if you provide a key function. In context, Raymond is referring to using itemgetter(0) as the key function, so only the first item in the tuple will be considered. -- Steve

Hi, On Sat, 30 Oct 2021 at 16:23, Raymond Bisdorff <raymond.bisdorff@pt.lu> wrote:
L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] L.sort(reverse=True) L [(3, 'e'), (2, 'd'), (2, 'b'), (1, 'c'), (1, 'a')]
it should be:
L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] reverseTuplesSort(L) [(3, 'e'), (2, 'b'), (2, 'd'), (1, 'a'), (1, 'c')]
No, it should not, because 'd'>'b' and 'c'>'a' ! tuple comparison compares both elements in lexicographic order.
Cheers, E

On 10/30/21 11:46 AM, Raymond Bisdorff wrote:
Dear Python developers,
The help(list) shows in a python console the following documentation string for the list.sort() method.
sort(self, /, *, key=None, reverse=False) | Sort the list in ascending order and return None. | | The sort is in-place (i.e. the list itself is modified) and stable (i.e. the | order of two equal elements is **maintained**).
Please notice the following inconsistency in Python3.10.0 and before of a sort(reverse=True) result:
L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] L.sort(reverse=True) L [(3, 'e'), (2, 'd'), (2, 'b'), (1, 'c'), (1, 'a')]
it should be:
L = [(1, 'a'), (2, 'b'), (1, 'c'), (2, 'd'), (3, 'e')] reverseTuplesSort(L) [(3, 'e'), (2, 'b'), (2, 'd'), (1, 'a'), (1, 'c')]
Same inconsistency appears when using a sorting key.
Passing easily unnoticed may produce unexpected and potentially wrong ranking results.
Best Regards, RB
Why do you thing (1,'a') should be 'bigger' than (1, 'c'), or (2,'b') bigger than (2,'d')? Tuples don't sort on just the first element, but on all their elements in order if the previous ones are the same. -- Richard Damon
participants (7)
-
Evpok Padding
-
Greg Ewing
-
Paul Ganssle
-
Raymond Bisdorff
-
Richard Damon
-
Steven D'Aprano
-
Tim Peters