18.1. 概览
由于代码优化过程中存在太多的不明确因素,以至于你很难清楚该从何入手。
让我们从这里开始:你真的确信你要这样做吗? 你的代码真的那么差吗?值得花时间去优化它吗?在你的应用程序的生命周期中,与花费在等待一个远程数据库服务器,或是等待用户输入相比,运行这段代码将花费多少时间?
第二,你确信已经完成代码编写了吗? 过早的优化就像是在一块半生不熟的蛋糕上撒糖霜。你花费了几小时、几天 (或更长) 时间来优化你的代码以提高性能,却发现它不能完成你希望它做的工作。那是浪费时间。
这并不是说代码优化毫无用处,但是你需要检查一下整个系统,并且确定把时间花在这上面是值得的。在优化代码上每花费一分钟,就意味着你少了增加新功能、编写文档或者陪你的孩子玩或者编写单元测试的一分钟。
哦,是的,单元测试。不必我说,在开始性能优化之前你需要一个完全的单元测试集。你最不需要的就是在乱动你的算法时引入新的问题。
谨记着这些忠告,让我们来看一些优化 Python 代码的技术。我们要研究的代码是 Soundex 算法的实现。Soundex 是一种 20 世纪在美国人口普查中归档姓氏的方法。它把听起来相似的姓氏归在一起,使得在即便错误拼写的情况下调查者仍能查找到。Soundex 今天仍然因差不多的原因被应用着,当然现在用计算机数据库服务器了。大部分的数据库服务器都有 Soundex 函数。
Soundex 算法有几个差别不大的变化版本。这是本章使用的:
- 名字的第一个字母不变。
根据特定的对照表,将剩下的字母转换为数字:
- 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。
去除连续重复。
- 去除所有 9。
- 如果结果都少于四个字符 (第一个字母加上后面的三位字符),就以零补齐。
- 如果结果超过四个字符,丢弃掉四位之后的字符。
比如,我的名字 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 发展的年代表以及地域变化。