《Python Cookbook(第2版)中文版》第4章引言由David Ascher撰写,本章包括了一些很通用的,可以应用到各处的技术,这些内容难于归结到某个类别。本节为大家介绍用一条语句完成赋值和测试。
5.2 不区分大小写对字符串列表排序
感谢:Kevin Altis、Robin Thomas、Guido van Rossum、Martin V. Lewis、Dave Cross
任务
你想对一个字符串列表排序,并忽略掉大小写信息。举个例子,你想要小写的a排在大写的B前面。默认的情况下,字符串比较是大小写敏感的(比如所有的大写字符排在小写字符之前)。
解决方案
采用decorate-sort-undecorate(DSU)用法既快又简单:
Python 2.4已经提供对DSU的原生支持了,因此(假设string_list的元素都是真正的普通字符串,而不是Unicode对象之类),可以用更简短更快的方式:
讨论
一个很明显的可选方案是编写一个比较函数,并将其传递给sort方法:
不过,在每次比较中,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),而不是返回一个排序完毕的拷贝且不对原列表做任何修改。如果你希望case_insensitive_sort函数也能够直接作用于原列表,只需要将return语句修改为对列表本体的赋值:
反过来,在Python 2.4中,如果你希望获得一个排序完毕的拷贝,且让原列表保持不变,可以使用新的内建的sorted函数。比如,在Python 2.4中:
上述代码打印列表中的每一个字符串,这些字符串根据大小写无关的规则进行排序,而且不会影响到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和count方法表现出与大小写无关的行为方式,需要在各种搜索匹配的任务中忽略掉大小写,等等。如果这是你的需求,那么真正需要的是str的一个子类型,从而在比较和哈希的时候忽略大小写-相比于实现各种容器和函数来满足这些需求,这是解决这类问题的最好的方法。参考1.24节内容,可以看到如何实现这样一种子类型。
更多资料
Python Frequently Asked Questions, http://www.python.org/cgi-bin/faqw.py?req=show&file= faq04.051.htp;5.3节;Python 2.4的Library Reference中关于sorted内建函数,sort和sorted的key参数;1.24节。