导航: 起始页 > Dive Into Python > 重构 > 重构 | << >> | ||||
Python 研究(Dive Into Python)Python 从新手到高手 [DIP_5_4_CPUG_RELEASE] |
全面的单元测试带来的最大好处不是你的全部测试用例最终通过时的成就感;也不是被责怪破坏了别人的代码时能够证明自己的自信。最大的好处是单元测试给了你自由去无情地重构。
重构是在可运行代码的基础上使之更良好工作的过程。 通常,“更好”意味着“更快”,也可能意味着 “使用更少的内存”,或者 “使用更少的磁盘空间”,或者仅仅是“更优雅的代码”。 不管对你,对你的项目意味什么,在你的环境中,重构对任何程序的长期良性运转都是重要的。
这里, “更好” 意味着 “更快”。更具体地说, fromRoman 函数可以更快,关键在于那个丑陋的、用于验证罗马数字有效性的正则表达式。尝试不用正则表达式去解决是不值得的(这样做很难,而且可能也快不了多少),但可以通过预编译正则表达式使函数提速。
例 15.10. 编译正则表达式
>>> import re >>> pattern = '^M?M?M?$' >>> re.search(pattern, 'M') <SRE_Match object at 01090490> >>> compiledPattern = re.compile(pattern) >>> compiledPattern <SRE_Pattern object at 00F06E28> >>> dir(compiledPattern) ['findall', 'match', 'scanner', 'search', 'split', 'sub', 'subn'] >>> compiledPattern.search('M') <SRE_Match object at 01104928>
在需要多次使用同一个正则表达式的情况下,应该将它进行编译以获得一个 pattern 对象,然后直接调用这个 pattern 对象的方法。 |
例 15.11. roman81.py 中已编译的正则表达式
这个文件可以在例子目录下的 py/roman/stage8/ 目录中找到。
如果您还没有下载本书附带的例子程序, 可以 下载本程序和其他例子程序。
# toRoman and rest of module omitted for clarity romanNumeralPattern = \ re.compile('^M?M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)(IX|IV|V?I?I?I?)$') def fromRoman(s): """convert Roman numeral to integer""" if not s: raise InvalidRomanNumeralError, 'Input can not be blank' if not romanNumeralPattern.search(s): raise InvalidRomanNumeralError, 'Invalid Roman numeral: %s' % s result = 0 index = 0 for numeral, integer in romanNumeralMap: while s[index:index+len(numeral)] == numeral: result += integer index += len(numeral) return result
那么编译正则表达式可以提速多少呢? 你自己来看吧:
例 15.12. 用 romantest81.py 测试 roman81.py 的结果
............. ---------------------------------------------------------------------- Ran 13 tests in 3.385s OK
我还想做另外一个性能优化工作。就正则表达式语法的复杂性而言,通常有不止一种方法来构造相同的表达式是不会令人惊讶的。 在 comp.lang.python 上对该模块进行一些讨论后,有人建议我使用 {m,n} 语法来查找可选重复字符。
例 15.13. roman82.py
这个文件可以在例子目录下的 py/roman/stage8/ 目录中找到。
如果您还没有下载本书附带的例子程序, 可以 下载本程序和其他例子程序。
# rest of program omitted for clarity #old version #romanNumeralPattern = \ # re.compile('^M?M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)(IX|IV|V?I?I?I?)$') #new version romanNumeralPattern = \ re.compile('^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$')
这样的正则表达简短一些 (虽然可读性不太好)。 核心问题是,是否能加快速度?
例 15.14. 以 romantest82.py 测试 roman82.py 的结果
............. ---------------------------------------------------------------------- Ran 13 tests in 3.315s OK
还有另外一个我想做的调整,我保证这是最后一个,之后我会停下来,让这个模块歇歇。就像你多次看到的,正则表达式越晦涩难懂越快,我可不想在六个月内再回头试图维护它。是呀!测试用例通过了,我便知道它工作正常,但如果我搞不懂它是如何工作的,添加新功能,修正新 Bug,或者维护它都将变得很困难。 正如你在 第 7.5 节 “松散正则表达式”, 看到的, Python 提供了逐行注释你的逻辑的方法。
例 15.15. roman83.py
该文件可以在例子目录下的 py/roman/stage8/ 目录中找到。
如果您还没有下载本书附带的例子程序, 可以 下载本程序和其他例子程序。
# rest of program omitted for clarity #old version #romanNumeralPattern = \ # re.compile('^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$') #new version romanNumeralPattern = re.compile(''' ^ # beginning of string M{0,4} # thousands - 0 to 4 M's (CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's), # or 500-800 (D, followed by 0 to 3 C's) (XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's), # or 50-80 (L, followed by 0 to 3 X's) (IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's), # or 5-8 (V, followed by 0 to 3 I's) $ # end of string ''', re.VERBOSE)
<< 应对需求变化 |
| 1 | 2 | 3 | 4 | 5 | |
后记 >> |