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

用 reverse 参数使用 sorted()

如 sorted()的 help() 文档所示,有一个名为 reverse 的可选关键字参数,它将根据分配给它的布尔值更改排序行为。如果将 reverse 指定为 True,则排序将按降序排列:

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

排序逻辑保持不变,这意味着名称仍按其第一个字母排序。但是,如果 reverse 关键字设置为 True,则输出反转。

如果指定了 False,则排序将保持升序。可以使用前面的任何示例来使用 True 或 False 来查看 reverse 的行为:

>>> names_with_case = ['harry', 'Suzy', 'al', 'Mark']
>>> sorted(names_with_case, reverse=True)
['harry', 'al', 'Suzy', 'Mark']
>>> similar_values = [False, 1, 'A' == 'B', 1 <= 0]
>>> sorted(similar_values, reverse=True)
[1, False, False, False]
>>> numbers = [6, 9, 3, 1]
>>> sorted(numbers, reverse=False)
[1, 3, 6, 9]

sorted() 使用 key 参数排序

sorted() 最强大的功能之一是一个叫做 key 的关键字参数。此参数需要将函数传递给它,并且该函数将用于要排序的列表中的每个值,以确定生成的顺序。

我们假设排序一个特定列表的要求是列表中字符串的长度,最短到最长。返回字符串长度 len() 的函数将与 key 参数一起使用:

>>> word = 'paper'
>>> len(word)
5
>>> words = ['banana', 'pie', 'Washington', 'book']
>>> sorted(words, key=len)
['pie', 'book', 'banana', 'Washington']

生成的顺序是一个字符串顺序最短到最长的列表。列表中每个元素的长度由 len 确定,然后以升序返回。

回到前面的例子,当大小写不同时按第一个字母排序。key 可以通过将整个字符串转换为小写来解决该问题:

>>> names_with_case = ['harry', 'Suzy', 'al', 'Mark']
>>> sorted(names_with_case)
['Mark', 'Suzy', 'al', 'harry']
>>> sorted(names_with_case, key=str.lower)
['al', 'harry', 'Mark', 'Suzy']

输出值尚未转换为小写,因为 key 不会处理原始列表中的数据。在排序期间,传递给 key 的函数将在每个元素上调用以确定排序顺序,但原始值仍将体现在输出中。使用带有 key 参数的函数时,有两个主要限制。

首先,传递给 key 的函数中参数的数量必须为 1。

下面的示例显示了带有两个参数的加法函数的定义。当该函数用于数字列表中的键时,它会失败,因为它缺少第二个参数。每次在排序期间调用 add() 时,它一次只从列表中接收一个元素:

>>> def add(x, y):
...     return x + y
... 
>>> values_to_add = [1, 2, 3]
>>> sorted(values_to_add, key=add)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: add() missing 1 required positional argument: 'y'

** 第二个限制是与 key 一起使用的函数必须能够处理 iterable 中的所有值。** 例如,有一个数字列表,表示为要在 sorted 中使用的字符串,而 key 将尝试将它们转换为使用 int。如果 iterable 中的值不能转换为整数,则该函数将失败:

>>> values_to_cast = ['1', '2', '3', 'four']
>>> sorted(values_to_cast, key=int)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'four'

作为字符串的每个数值都可以转换为 int,但是 four 不能。这会导致引发 ValueError 并解释 four 无法转换为 int,因为它无效。

key 功能非常强大,因为几乎任何内置或用户定义的函数都可用于操作输出顺序。

如果排序要求是按每个字符串中的最后一个字母排序可迭代(如果字母相同,然后使用下一个字母),则可以定义函数,然后在排序中使用。下面的示例定义了一个函数,该函数反转传递给它的字符串,然后该函数用作键的参数:

>>> def reverse_word(word):
...     return word[::-1]
...
>>> words = ['banana', 'pie', 'Washington', 'book']
>>> sorted(words, key=reverse_word)
['banana', 'pie', 'book', 'Washington']

word[::-1] 切片语法用于反转字符串。每个元素都会应用 reverse_word(),排序顺序将基于后向单词中的字符。

当然,也可以使用 key 参数中定义的 lambda 函数,而不是编写独立函数。 lambda 匿名函数:1)必须内联定义;2)没有名字;3)不能包含 statement;4)像函数一样执行。

在下面的示例中,key 被定义为没有名称的 lambda,lambda 采用的参数是 x,然后 x [:: -1] 是将对参数执行的操作:

>>> words = ['banana', 'pie', 'Washington', 'book']
>>> sorted(words, key=lambda x: x[::-1])
['banana', 'pie', 'book', 'Washington']

在每个元素上调用 x [::-1] 并反转该单词。然后将反转的输出用于排序,但仍返回原始单词。 如果需求发生变化,要求顺序也应该反转,那么 reverse 关键字可以与 key 参数一起使用:

>>> words = ['banana', 'pie', 'Washington', 'book']
>>> sorted(words, key=lambda x: x[::-1], reverse=True)
['Washington', 'book', 'pie', 'banana']

当需要基于属性对类对象进行排序时,lambda 函数也很有用。如果有一组学生并需要按最终成绩(从最高到最低)对其进行排序,则可以使用 lambda 从该课程中获取成绩属性:

>>> from collections import namedtuple

>>> StudentFinal = namedtuple('StudentFinal', 'name grade')
>>> bill = StudentFinal('Bill', 90)
>>> patty = StudentFinal('Patty', 94)
>>> bart = StudentFinal('Bart', 89)
>>> students = [bill, patty, bart]
>>> sorted(students, key=lambda x: getattr(x, 'grade'), reverse=True)
[StudentFinal(name='Patty', grade=94), StudentFinal(name='Bill', grade=90), StudentFinal(name='Bart', grade=89)]

此示例使用 namedtuple 生成具有 name 和 grade 属性的类。 lambda 在每个元素上调用 getattr() 并返回 grade 的值。 reverse 设置为 True 以使升序输出转为降序,以便首先排序最高等级。

当在 sorted() 上同时使用 key 和 reverse 关键字参数时,如何进行排序的可能性是无穷无尽的。当对一个小函数使用基本 lambda 时,代码可以保持干净和简短,或者可以编写一个全新的函数导入,并在 key 参数中使用它。