Python Cookbook-5.2 不区分大小写对字符串列表排序
任务
你想对一个字符串列表排序,并忽略掉大小写信息。举个例子,你想要小写的a排在大写的 B 前面。默认的情况下,字符串比较是大小写敏感的(比如所有的大写字符排在小写字符之前)。
解决方案
采用 decorate-sort-undecorate(DSU)用法既快又简单:
def case_insensitive_sort(string_list):
auxiliary_list = [(x.lower(),x) for x in string_list] #decorate
auxiliary_list.sort() #sort
return [x[1] for x in auxiliary_list] #undecorate
Python 2.4已经提供对 DSU的原生支持了,因此(假设 string_list 的元素都是真正的普通字符串,而不是 Unicode 对象之类),可以用更简短更快的方式:
def case_insensitive_sort(string_list):
return sorted(string_list,key=str.lower)
讨论
一个很明显的可选方案是编写一个比较函数,并将其传递给sort方法:
def case_insensitive_sort_1(string_list):
def compare(a,b):return cmp(a.lower(),b.lower())
string_list.sort(compare)
不过,在每次比较中,lower方法都会被调用两次,而对于长度为n的列表来说,比较的次数与 nlog(n)成正比。
DSU 方法则创建了一个辅助的列表,每个元素都是元组,元组的元素则来自原列表并被当做“键”处理。这个排序是基于键的,因为Python 的元组的比较是根据条目顺序进行的(比如,它会首先比较元组的第一个元素)。要将一个长度为n的字符串列表排序,配合 DSU的使用,lower方法只需要被调用n次,因而在第一步,decorate 阶段,以及最后一步,undecorate阶段节省了很多时间。
DSU 有时也被称为——但这种叫法不是很准确——Schwartzian变换,这是对Perl的一个著名应用的一个不太准确的类比。(如果要说相似,DSU 更接近于 Guttman-Rosler 变换,见http://www.sysarch.com/perl/sort_paper.html。)
DSU 是如此重要,因此 Python 2.4提供了对它的直接支持。可以给列表的 sort 方法传递一个可选的命名参数key,而且它可以被调用,作用于列表中的每个元素并获得用于排序的键。如果传递这样的一个参数,排序会在内部使用DSU。因此,在Python 2.4中string_list.sort(key=str.lower 实际上等价于 case_insensitive_sort 函数,只不过 sort 方法会直接作用于原列表(且返回 None),而不是返回一个排序完毕的拷贝且不对原列表做任何修改。如果你希望caseinsensitive sort 函数也能够直接作用于原列表,只需要将retumn 语句修改为对列表本体的赋值:
string_list{:] = [x[1] for x in auxiliary_list]
反过来,在 Python 2.4中,如果你希望获得一个排序完毕的拷贝,且让原列表保持不变
可以使用新的内建的 sorted 函数。比如,在Python2.4中:
for s in sorted(string_list,key = str.lower):print s
上述代码打印列表中的每一个字符串,这些字符串根据大小写无关的规则进行排序而且不会影响到 string_list 本身。
在 Python 2.4 的解决方案中,将 str.lower 作为 key 参数限制了你以特定的方式将字符串排序(不包括 Unicode 对象)。如果你知道你正在排序的是 Unicode 对象列表,可以使用 key=unicode.lower。如果你希望函数能够同时适用于普通字符串和 Unicode 对象,可以import string 并使用 key= string.lower;另外,也可以使用 key = lambda s: s.lower( )。
如果需要对字符串列表进行大小写无关的排序,可能也需要用大小写无关的字符串作为键的字典和集合,需要列表对 index 和 coumt 方法表现出与大小写无关的行为方式,需要在各种搜索匹配的任务中忽略掉大小写,等等。如果这是你的需求,那么真正需要的是 s的一个子类型,从而在比较和哈希的时候忽略大小写——相比于实现各种容器和函数来满足这些需求,这是解决这类问题的最好的方法。参考 1.24 节内容,可以看到如何实现这样一种子类型。