第 18 章 性能优化
Description: Python from novice to pro
Keywords: Python, Dive Into Python, tutorial, object-oriented, programming, documentation, book, free

# 深入 Python :Dive Into Python 中文版 Python 从新手到专家 [Dip_5.4b_CPyUG_Release] | Find:

## 第 18 章 性能优化

性能优化 (Performance tuning) 是一件多姿多彩的事情。Python 是一种解释性语言并不表示你不应该担心代码优化。但也不必太 担心。

## 18.1. 概览

由于代码优化过程中存在太多的不明确因素，以至于你很难清楚该从何入手。

让我们从这里开始：你真的确信你要这样做吗？ 你的代码真的那么差吗？值得花时间去优化它吗？在你的应用程序的生命周期中，与花费在等待一个远程数据库服务器，或是等待用户输入相比，运行这段代码将花费多少时间？

第二，你确信已经完成代码编写了吗？ 过早的优化就像是在一块半生不熟的蛋糕上撒糖霜。你花费了几小时、几天 (或更长) 时间来优化你的代码以提高性能，却发现它不能完成你希望它做的工作。那是浪费时间。

这并不是说代码优化毫无用处，但是你需要检查一下整个系统，并且确定把时间花在这上面是值得的。在优化代码上每花费一分钟，就意味着你少了增加新功能、编写文档或者陪你的孩子玩或者编写单元测试的一分钟。

哦，是的，单元测试。不必我说，在开始性能优化之前你需要一个完全的单元测试集。你最不需要的就是在乱动你的算法时引入新的问题。

谨记着这些忠告，让我们来看一些优化 Python 代码的技术。我们要研究的代码是 Soundex 算法的实现。Soundex 是一种 20 世纪在美国人口普查中归档姓氏的方法。它把听起来相似的姓氏归在一起，使得在即便错误拼写的情况下调查者仍能查找到。Soundex 今天仍然因差不多的原因被应用着，当然现在用计算机数据库服务器了。大部分的数据库服务器都有 Soundex 函数。

Soundex 算法有几个差别不大的变化版本。这是本章使用的：

1. 名字的第一个字母不变。
2. 根据特定的对照表，将剩下的字母转换为数字：
   - B、 F、 P 和 V 转换为 1。
   - C、 G、 J、 K、 Q、 S、 X 和 Z 转换为 2。
   - D 和 T 转换为 3。
   - L 转换为 4。
   - M 和 N 转换为 5。
   - R 转换为 6。
   - 所有其他字母转换为 9。
3. 去除连续重复。
4. 去除所有 9。
5. 如果结果都少于四个字符 (第一个字母加上后面的三位字符)，就以零补齐。
6. 如果结果超过四个字符，丢弃掉四位之后的字符。

比如，我的名字 `Pilgrim` 被转换为 P942695。没有连续重复，所以这一步不需要做。然后是去除 9，剩下 P4265。太长了，所以你把超出的字符丢弃，剩下 P426。

另一个例子：`Woo` 被转换为 W99，变成 W9，变成 W，然后以补零成为 W000。

这是 Soundex 函数的第一次尝试：

### 例 18.1. `soundex/stage1/soundex1a.py`

如果您还没有下载本书附带的样例程序, 可以 下载本程序和其他样例程序。

    import string, re

    charToSoundex = {"A": "9",
                     "B": "1",
                     "C": "2",
                     "D": "3",
                     "E": "9",
                     "F": "1",
                     "G": "2",
                     "H": "9",
                     "I": "9",
                     "J": "2",
                     "K": "2",
                     "L": "4",
                     "M": "5",
                     "N": "5",
                     "O": "9",
                     "P": "1",
                     "Q": "2",
                     "R": "6",
                     "S": "2",
                     "T": "3",
                     "U": "9",
                     "V": "1",
                     "W": "9",
                     "X": "2",
                     "Y": "9",
                     "Z": "2"}

    def soundex(source):
        "convert string to Soundex equivalent"

        # Soundex requirements:
        # source string must be at least 1 character
        # and must consist entirely of letters
        allChars = string.uppercase + string.lowercase
        if not re.search('^[%s]+$' % allChars, source):
            return "0000"

        # Soundex algorithm:
        # 1. make first character uppercase
        source = source[0].upper() + source[1:]

        # 2. translate all other characters to Soundex digits
        digits = source[0]
        for s in source[1:]:
            s = s.upper()
            digits += charToSoundex[s]

        # 3. remove consecutive duplicates
        digits2 = digits[0]
        for d in digits[1:]:
            if digits2[-1] != d:
                digits2 += d

        # 4. remove all "9"s
        digits3 = re.sub('9', '', digits2)

        # 5. pad end with "0"s to 4 characters
        while len(digits3) < 4:
            digits3 += "0"

        # 6. return first 4 characters
        return digits3[:4]

    if __name__ == '__main__':
        from timeit import Timer
        names = ('Woo', 'Pilgrim', 'Flingjingwaller')
        for name in names:
            statement = "soundex('%s')" % name
            t = Timer(statement, "from __main__ import soundex")
            print name.ljust(15), soundex(name), min(t.repeat())

### 进一步阅读

- Soundexing and Genealogy 给出了 Soundex 发展的年代表以及地域变化。