Python 比较任何类型的序列时,会一一比较序列里的各个元素。对字符串来说,比较的是码位。可是在比较非 ASCII 字符时,得到的结果不尽如人意。

下面对一个生长在巴西的水果的列表进行排序:

>>> fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
>>> sorted(fruits)
['acerola', 'atemoia', 'açaí', 'caju', 'cajá']

不同的区域采用的排序规则有所不同,葡萄牙语等很多语言按照拉丁字母表排序,重音符号和下加符对排序几乎没什么影响。9 因此,排序时“cajá”视作“caja”,必定排在“caju”前面。

9变音符号对排序有影响的情况很少发生,只有两个词之间唯有变音符号不同时才有影响。此时,带有变音符号的词排在常规词的后面。

排序后的 fruits 列表应该是:

['açaí', 'acerola', 'atemoia', 'cajá', 'caju']

在 Python 中,非 ASCII 文本的标准排序方式是使用 locale.strxfrm 函数,根据 locale 模块的文档(https://docs.python.org/3/library/locale.html?highlight=strxfrm#locale.strxfrm),这 个函数会“把字符串转换成适合所在区域进行比较的形式”。

使用 locale.strxfrm 函数之前,必须先为应用设定合适的区域设置,还要祈祷操作系统支持这项设置。在区域设为 pt_BR 的 GNU/Linux(Ubuntu 14.04)中,可以使用示例 4-19 中的命令。

示例 4-19 使用 locale.strxfrm 函数做排序键

>>> import locale
>>> locale.setlocale(locale.LC_COLLATE, 'pt_BR.UTF-8')
'pt_BR.UTF-8'
>>> fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
>>> sorted_fruits = sorted(fruits, key=locale.strxfrm)
>>> sorted_fruits
['açaí', 'acerola', 'atemoia', 'cajá', 'caju']

因此,使用 locale.strxfrm 函数做排序键之前,要调用 setlocale(LC_COLLATE, «your_locale»)

不过,有几点要注意。

10在 Linux 操作系统中,中国大陆的读者可以使用 zh_CN.UTF-8,简体中文会按照汉语拼音顺序进行排序,它也能对葡萄牙语进行正确排序。——编者注

11感谢 Leonardo Rochael,他所做的工作超出了身为技术审校的职责,虽然他是 GNU/Linux 用户,但却研究了这些 Windows 细节。

12同样,我没找到解决方案,不过却发现其他人也报告了同样的问题。本书技术审校之一 Alex Martelli,在他装有 OS X 10.9 的 Mac 电脑中使用 setlocalelocale.strxfrm 时没有遇到问题。综上:结果因人而异。

因此,标准库提供的国际化排序方案可用,但是似乎只支持 GNU/Linux(可能也支持 Windows,但你得是专家)。即便如此,还要依赖区域设置,而这会为部署带来问题。

幸好,有个较为简单的方案:PyPI 中的 PyUCA 库。

James Tauber,一位高产的 Django 贡献者,他一定是感受到了这一痛点,因此开发了 PyUCA 库(https://pypi.python.org/pypi/pyuca/),这是 Unicode 排序算法(Unicode Collation Algorithm,UCA)的纯 Python 实现。示例 4-20 展示了它的简单用法。

示例 4-20 使用 pyuca.Collator.sort_key 方法

>>> import pyuca
>>> coll = pyuca.Collator()
>>> fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
>>> sorted_fruits = sorted(fruits, key=coll.sort_key)
>>> sorted_fruits
['açaí', 'acerola', 'atemoia', 'cajá', 'caju']

这样做更友好,而且恰好可用。我在 GNU/Linux、OS X 和 Windows 中做过测试。目前,PyUCA 只支持 Python 3.x13

132015 年 5 月,PyUCA 重新支持 Python 2.x,参见:http://jktauber.com/2015/05/13/pyuca-supports-python-2-again。——编者注

PyUCA 没有考虑区域设置。如果想定制排序方式,可以把自定义的排序表路径传给 Collator() 构造方法。PyUCA 默认使用项目自带的 allkeys.txthttps://github.com/jtauber/pyuca),这就是 Unicode 6.3.0 的“Default Unicode Collation Element Table”(http://www.unicode.org/Public/UCA/6.3.0/allkeys.txt)的副本。

顺便说一下,那个表是 Unicode 数据库中众多表中的一个。下一节会讨论这个话题。