Python 排序——sorted 与 sort 用法 (二)

Python 排序的局限性和陷阱

当使用 Python 对整数值进行排序时,可能会出现一些限制和奇怪的现象。
1. 具有不能比较数据类型的列表无法进行排序
有些数据类型使用 sorted 是无法进行比较的,因为它们的类型不同。如果尝试在包含不可比较数据的列表上使用 sorted(),Python 将返回错误。在此示例中,由于不兼容性,无法对同一列表中的 None 和 int 进行排序:

>>> mixed_types = [None, 0]
>>> sorted(mixed_types)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'int' and 'NoneType'

此错误显示了为什么 Python 无法对给定的值进行排序。它试图通过使用小于运算符(<)来确定值,以确定排序顺序中哪个值较低。例如,数字 1 应该出现在苹果这个词之前吗?但是,如果迭代器包含所有数字的整数和字符串的组合,则可以使用列表推导将它们强制转换为可比较的数据类型:

>>> mixed_numbers = [5, "1", 100, "34"]
>>> sorted(mixed_numbers)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'str' and 'int'
>>> # List comprehension to convert all values to integers
>>> [int(x) for x in mixed_numbers]
[5, 1, 100, 34]
>>> sorted([int(x) for x in mixed_numbers])
[1, 5, 34, 100]

mixed_numbers 中的每个元素都调用了 int()来将任何 str 值转换为 int 值。然后调用 sorted() 并成功比较每个元素并提供排序的输出。

另外,Python 还可以隐式地将值转换为另一种类型。在下面的示例中,1 <= 0 的评估是 false 语句,因此评估的输出将为 False。数字 1 可以转换为 True 作为 bool 类型,而 0 转换为 False。

即使列表中的元素看起来不同,它们也可以全部转换为布尔值(True 或 False)并使用 sorted() 进行相互比较:

>>> similar_values = [False, 0, 1, 'A' == 'B', 1 <= 0]
>>> sorted(similar_values)
[False, 0, False, False, 1]

‘A’==’B’和 1 <= 0 转换为 False 并在有序输出中返回。

此示例说明了排序的一个重要方面:** 排序稳定性。** 在 Python 中,当你对相等的值进行排序时,它们将在输出中保留其原始顺序。即使 1 移动,所有其他值都相等,它们保持相对于彼此的原始顺序。在下面的示例中,所有值都被视为相等,并将保留其原始位置:

>>> false_values = [False, 0, 0, 1 == 2, 0, False, False]
>>> sorted(false_values)
[False, 0, 0, False, 0, False, False]

如果检查原始顺序和排序输出,可以看到 1 == 2 转换为 False,所有排序输出都是原始顺序。

2. 当排序字符串时,大小写很重要
sorted() 可用于字符串列表,以按升序对值进行排序,默认情况下按字母顺序排列:

>>> names = ['Harry', 'Suzy', 'Al', 'Mark']
>>> sorted(names)
['Al', 'Harry', 'Mark', 'Suzy']

但是,Python 使用每个字符串中第一个字母的 Unicode 代码点来确定升序排序。意思是 sorted()不会将名称 Al 和 al 视为相同。此示例使用 ord() 返回每个字符串中第一个字母的 Unicode 代码点:

>>> names_with_case = ['harry', 'Suzy', 'al', 'Mark']
>>> sorted(names_with_case)
['Mark', 'Suzy', 'al', 'harry']
>>> # 每个word中第一个字母的unicode代码点列表推导式
>>> [(ord(name[0]), name[0]) for name in sorted(names_with_case)]
[(77, 'M'), (83, 'S'), (97, 'a'), (104, 'h')]

name [0] 返回 sorted(names_with_case) 的每个元素中的第一个字符,ord() 提供 Unicode 代码点。即使 a 在字母表中的 M 之前,M 的代码点在 a 之前,因此排序的输出首先是 M。

如果第一个字母相同,则 sorted() 将使用第二个字符来确定顺序,第三个字符等,依此类推,一直到字符串的结尾:

>>> very_similar_strs = ['hhhhhd', 'hhhhha', 'hhhhhc','hhhhhb']
>>> sorted(very_similar_strs)
['hhhhha', 'hhhhhb', 'hhhhhc', 'hhhhhd']

除最后一个字符外,very_similar_strs 的每个值都相同。 sorted() 比较字符串,因为前五个字符相同,输出将基于第六个字符。

包含相同值的字符串将最终排序为最短到最长,因为较短的字符串没有要与较长字符串进行比较的元素:

>>> different_lengths = ['hhhh', 'hh', 'hhhhh','h']
>>> sorted(different_lengths)
['h', 'hh', 'hhhh', 'hhhhh']

最短的字符串 h 排序第一,最长的字符串 hhhhh 排序最后。