文章来自《Python cookbook》.

翻译仅仅是为了个人学习,其它商业版权纠纷与此无关!

-- Zoom.Quiet [2004-08-11 16:51:29]

1. 通过两个字段来排序

2.5 Sorting by One Field, Then by Another 通过两个字段来排序

Credit: José Sebrosa

1.1. 问题 Problem

You need to sort a list by more than one field of each item.

你需要通过每个条目不至一个的字段来排序一个list

1.2. 解决 Solution

Passing a comparison function to a list's sort method is slow for lists of substantial size, but it can still be quite handy when you need to sort lists that are reasonably small. In particular, it offers a rather natural idiom to sort by more than one field:

传递一个比较函数到一个list的排序方法里对固定尺寸list来说是慢的。但是当你需要排序相当小的list的时候,它仍然能够相当方便。特别的,在排序多个字段时,它提供了一个相当自然的惯例.

   1 import string
   2 
   3 star_list = ['Elizabeth Taylor', 'Bette Davis', 'Hugh Grant', 'C. Grant']
   4 
   5 star_list.sort(lambda x,y: (
   6    cmp(string.split(x)[-1], string.split(y)[-1]) or  # Sort by last name...
   7    cmp(x, y)))                                       # ...then by first name
   8 
   9 print "Sorted list of stars:"
  10 for name in star_list:
  11     print name

1.3. 讨论 Discussion

This recipe uses the properties of the cmp built-in function and the or operator to produce a compact idiom for sorting a list over more than one field of each item.

为了对一个list每个条目上不至一个字段来排序,这个配方使用内建函数cmp的特性和or操作符来产生一个紧凑的用法。

cmp(X, Y) returns false (0) when X and Y compare equal, so only in these cases does or let the next call to cmp happen. To reverse the sorting order, simply swap X and Y as arguments to cmp.

当X和Y相等,cmp(X, Y)返回false (0)。所以仅仅在这些情况下or才会让下一个cmp发生。为了倒转排序顺序,简单的交换作为cmp参数的X 和Y。

The fundamental idea of this recipe can also be used if another sorting criterion is associated with the elements of the list. We simply build an auxiliary list of tuples to pack the sorting criterion together with the main elements, then sort and unpack the result. This is more akin to the DSU idiom:

如果排序标准是同list的元素相关的,这个配方的基本思想也能被使用。 我们简单的构造一个辅助的tuple的list去把排序标准和主要元素包装在一起,然后,排序和解包结果。这个更类似于DSU用法。

   1 def sorting_criterion_1(data):
   2     return string.split(data)[-1]   # This is again the last name
   3 
   4 def sorting_criterion_2(data):
   5     return len(data)                # This is some fancy sorting criterion
   6 
   7 # Pack an auxiliary list:
   8 aux_list = map(lambda x: (x,
   9                           sorting_criterion_1(x),
  10                           sorting_criterion_2(x)),
  11                star_list)
  12 
  13 # Sort:
  14 aux_list.sort(lambda x,y: (
  15    cmp(x[1], y[1])  or       # Sort by criteria 1 (last name)...
  16    cmp(y[2], x[2])  or       # ...then by criteria 2 (in reverse order)...
  17    cmp(x, y)))               # ...then by the value in the main list
  18 
  19 # Unpack the resulting list:
  20 star_list = map(lambda x: x[0], aux_list)
  21 
  22 print "Another sorted list of stars:"
  23 for name in star_list:
  24     print name

Of course, once we're doing decorating, sorting, and undecorating, it may be worth taking a little extra trouble to be able to call the sort step without a comparison function (the DSU idiom), which will speed up the whole thing quite a bit for lists of substantial size. After all, packing the fields to be compared in the right order in each decorated tuple and plucking out the right field again in the undecorate step is pretty simple:

当然,一旦我么正在修饰,排序和除去修饰,它可以值得花费一点额外的麻烦来调用排序step而不用比较函数(DSU用法),对于相当小的list来说,它将提速相当多。毕竟,在每个装饰的tuple里,包装的字段按正确顺序被比较和在除去装饰步骤里,在一次采集出正确的字段是相当简单的。

   1 # Pack a better-ordered auxiliary list:
   2 aux_list = map(lambda x: (sorting_criterion_1(x),
   3                           sorting_criterion_2(x),
   4                           x),
   5                star_list)
   6 
   7 # Sort in a much simpler and faster way:
   8 aux_list.sort(  )
   9 
  10 # Unpack the resulting list:
  11 star_list = map(lambda x: x[-1], aux_list)

However, this doesn't deal with the reverse order, which you can easily obtain when passing a comparison function to sort by just switching arguments to cmp. To use DSU instead, you need to pack a suitably altered value of the criterion field. For a numeric field, changing the sign is fine. In this example, the sorting_criterion_2 that needs reverse sorting is indeed a number, so our task is easy:

然而,它不用处理反向顺序。当你传递一个比较函数去排序的时候仅仅交换cmp的参数就可以很容易的得到它。

   1 # Pack a better-ordered auxiliary list yielding the desired order:
   2 aux_list = map(lambda x: (sorting_criterion_1(x),
   3                           -sorting_criterion_2(x),
   4                           x),
   5                star_list)

For reverse sorting on a string field with DSU, you need a string-translation operation that maps each chr(x) into chr(255-x)梠r an even wider translation table for Unicode strings. It is a bit of a bother, but you only have to write it once. For example, for plain old strings:

为了用DSU在一个字符串字段反转排序,你需要一个映射chr(x)到ch(255-x)的字符串转换操作,对于Unicode字符串你甚至需要一个转换表。这有一点烦人但是你仅仅只写它一次。例如,对于简单的老式字符串。

   1 import string
   2 all_characters = string.maketrans('','')
   3 all_characters_list = list(all_characters)
   4 all_characters_list.reverse(  )
   5 rev_characters = ''.join(all_characters_list)
   6 rev_trans = string.maketrans(all_characters, rev_characters)

Now, if we want to reverse the first sorting criterion: 现在,如果我们想反转第一个排序的标准:

   1 # Pack a better-ordered and corrected auxiliary list:
   2 aux_list = map(lambda x: (string.translate(sorting_criterion_1(x), rev_trans),
   3                           sorting_criterion_2(x),
   4                           x),
   5                star_list)
   6 
   7 # Sort in a much simpler and faster way AND get just the desired result:
   8 aux_list.sort(  )
   9 
  10 # Unpack the resulting list:
  11 star_list = map(lambda x: x[-1], aux_list)

1.4. 参考 See Also

The Reference Manual section on sequences, and the subsection on mutable sequences (such as lists).