30.2.4. 使用sort来表示复杂的排序

列表类型提供了叫sort的方法,可以根据多项指标给list实例中的元素排序。默认按照升序排序。

In [62]: numbers = [93, 86, 11, 68, 70]

In [63]: numbers.sort()

In [64]: print(numbers)
[11, 68, 70, 86, 93]

那么,一般对象该如何排序呢?比如定义以Tool类表示各种建筑工具,它带有__repr__方法:

In [1]: class Tool:
   ...:     def __init__(self, name, weight):
   ...:         self.name = name
   ...:         self.weight = weight
   ...:
   ...:     def __repr__(self):
   ...:         return f'Tool({self.name!r}, {self.weight})'
   ...:
   ...: tools = [
   ...:     Tool('level', 3.5),
   ...:     Tool('hammer', 1.25),
   ...:     Tool('screwdriver', 0.5),
   ...:     Tool('chisel', 0.25),
   ...: ]
   ...:
   ...:

In [2]: print('Unsorted:', repr(tools))
Unsorted: [Tool('level', 3.5), Tool('hammer', 1.25), Tool('screwdriver', 0.5), Tool('chisel', 0.25)]

In [3]: tools.sort(key=lambda x: x.name)

In [4]: print('\nSorted:  ', tools)

Sorted:   [Tool('chisel', 0.25), Tool('hammer', 1.25), Tool('level', 3.5), Tool('screwdriver', 0.5)]

如果想改用另一项标准,比如用weight来排序,那只需要再定义一个lambda函数:

In [5]: tools.sort(key=lambda x: x.weight)
   ...: print('By weight:', tools)
   ...:
   ...:
By weight: [Tool('chisel', 0.25), Tool('screwdriver', 0.5), Tool('hammer', 1.25), Tool('level', 3.5)]

对于字符串这样的基本类型,我们可能需要通过key函数先对它的内容做一些变换,并根据变换之后的结果来排序。

In [6]: places = ['home', 'work', 'New York', 'Paris']
   ...: places.sort()
   ...: print('Case sensitive:  ', places)
   ...: places.sort(key=lambda x: x.lower())
   ...: print('Case insensitive:', places)
   ...:
   ...:
Case sensitive:   ['New York', 'Paris', 'home', 'work']
Case insensitive: ['home', 'New York', 'Paris', 'work']

要点:

列表的sort方法可以根据自然顺序给其中的字符串、整数、元组等内置类型的元素进行排序。

普通对象如果通过特殊方法定义了自然顺序,那么也可以用sort方法来排列,但这样的对象并不多见。可以把辅助函数传给sort方法的key参数,让sort根据这个函数所返回的值来排列元素顺序,而不是根据元素本身来排列。

如果排序时要依据的指标有很多项,可以把它们放在一个元组中,让key函数返回这样的元组。

对于支持一元减操作符的类型来说,可以单独给这项指标取反,让排序算法在这项指标上按照相反的方向处理。

如果这些指标不支持一元减操作符,可以多次调用sort方法,并在每次调用时分别指定key函数与reverse参数。

最次要的指标放在第一轮处理,然后逐步处理更为重要的指标,首要指标放在最后一轮处理。