楼主: lastwinner

[参考文档] Python 研究(Dive Into Python)

[复制链接]
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
291#
 楼主| 发表于 2006-7-19 00:45 | 只看该作者
例 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})$')
  你已经将 M?M?M?M? 替换为 M{0,4}。 它们的含义相同: “匹配 0 到 4 个 M 字符”。 类似地, C?C?C? 改成了 C{0,3} (“匹配 0 到 3 个 C 字符”) 接下来的 X 和 I 也一样。

这样的正则表达简短一些 (虽然可读性不太好)。 核心问题是,是否能加快速度?

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
292#
 楼主| 发表于 2006-7-19 00:45 | 只看该作者
例 15.14. 以 romantest82.py 测试 roman82.py 的结果
.............
----------------------------------------------------------------------
Ran 13 tests in 3.315s

OK                       总体而言, 这种正则表达使单元测试提速 2%。 这不太令人振奋,但记住 search 函数只是整体单元测试的一个小部分,很多时间花在了其他方面。 (我另外的测试表明这个应用了新语法的正则表达式使 search 函数提速 11% 。) 通过预先编译和使用新语法重写可以使正则表达式的性能提升超过 60%,另单元测试的整体性能提升超过 10%.
  比任何的性能提升更重要的是模块仍然运转完好。 这便是我早先提到的自由:自由地调整、修改或者重写任何部分并且保证在此过程中没有把事情搞得一团糟。 这并不是给只是为了调整代码而无休止地调整以许可,你有很切实的目标(“让 fromRoman 更快”),而且你可以实现这个目标,不会因为考虑在改动过程中是否会引入新的 Bug 而有所迟疑。

还有另外一个我想做的调整,我保证这是最后一个,之后我会停下来,让这个模块歇歇。就像你多次看到的,正则表达式越晦涩难懂越快,我可不想在六个月内再回头试图维护它。是呀!测试用例通过了,我便知道它工作正常,但如果我搞不懂它是如何工作的,添加新功能,修正新 Bug,或者维护它都将变得很困难。 正如你在 第 7.5 节 “松散正则表达式”, 看到的, Python 提供了逐行注释你的逻辑的方法。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
293#
 楼主| 发表于 2006-7-19 00:45 | 只看该作者
例 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)
  re.compile 函数的第二个参数是可选的,这个参数通过一个或一组选项设置来控制预编译正则表达式的多样化选项。这里你指定了 re.VERBOSE 选项,告诉 Python 正则表达式里有行内的注释。注释和他们周围的空白不会被认做正则表达式的一部分,在编译正则表达式时 re.compile 函数会忽略它们。这个新 “verbose” 版本与老版本完全一样,只是更具可读性。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
294#
 楼主| 发表于 2006-7-19 00:48 | 只看该作者
例 15.16. 用 romantest83.py 测试 roman83.py 的结果
.............
----------------------------------------------------------------------
Ran 13 tests in 3.315s

OK                       新 “verbose” 版本和老版本的运行速度一样。 事实上, 编译的 pattern 对象也一样,因为 re.compile 函数剔除掉所有你添加的内容。
  新 “verbose” 版本可以通过所有老版本通过的测试。 什么都没有改变,但在六个月后重读该模块的程序员却有了理解功能如何实现的机会。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
295#
 楼主| 发表于 2006-7-19 00:49 | 只看该作者
15.4. 后记
聪明的读者在学习 前一节 时想得会更深入一层。 现在写的这个程序中最令人头痛(性能负担)的是正则表达式,它是必需的,因为没有其它方法来处理罗马数字。但是,它们只有 5000 个,为什么不一次性地构建一个查询表来读取? 不必用正则表达式凸现了这个主意的好处。 你建立了整数到罗马数字查询表的时候,罗马数字到整数的逆向查询表也构建了。

更大的好处在于,你已经拥有一整套完全的单元测试。 你修改了多半的代码,但单元测试还是一样的,因此你可以确定你的新代码与来的代码一样可以正常工作。

例 15.17. roman9.py
这个文件可以在例子目录下的 py/roman/stage9/ 目录中找到。

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

[PHP]
#Define exceptions
class RomanError(Exception): pass
class OutOfRangeError(RomanError): pass
class NotIntegerError(RomanError): pass
class InvalidRomanNumeralError(RomanError): pass

#Roman numerals must be less than 5000
MAX_ROMAN_NUMERAL = 4999

#Define digit mapping
romanNumeralMap = (('M',  1000),
                   ('CM', 900),
                   ('D',  500),
                   ('CD', 400),
                   ('C',  100),
                   ('XC', 90),
                   ('L',  50),
                   ('XL', 40),
                   ('X',  10),
                   ('IX', 9),
                   ('V',  5),
                   ('IV', 4),
                   ('I',  1))

#Create tables for fast conversion of roman numerals.
#See fillLookupTables() below.
toRomanTable = [ None ]  # Skip an index since Roman numerals have no zero
fromRomanTable = {}

def toRoman(n):
    """convert integer to Roman numeral"""
    if not (0 < n <= MAX_ROMAN_NUMERAL):
        raise OutOfRangeError, "number out of range (must be 1..%s)" % MAX_ROMAN_NUMERAL
    if int(n) <> n:
        raise NotIntegerError, "non-integers can not be converted"
    return toRomanTable[n]

def fromRoman(s):
    """convert Roman numeral to integer"""
    if not s:
        raise InvalidRomanNumeralError, "Input can not be blank"
    if not fromRomanTable.has_key(s):
        raise InvalidRomanNumeralError, "Invalid Roman numeral: %s" % s
    return fromRomanTable

def toRomanDynamic(n):
    """convert integer to Roman numeral using dynamic programming"""
    result = ""
    for numeral, integer in romanNumeralMap:
        if n >= integer:
            result = numeral
            n -= integer
            break
    if n > 0:
        result += toRomanTable[n]
    return result

def fillLookupTables():
    """compute all the possible roman numerals"""
    #Save the values in two global tables to convert to and from integers.
    for integer in range(1, MAX_ROMAN_NUMERAL + 1):
        romanNumber = toRomanDynamic(integer)
        toRomanTable.append(romanNumber)
        fromRomanTable[romanNumber] = integer

fillLookupTables()

。。。。。。。。。。。。。
[/PHP]

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
296#
 楼主| 发表于 2006-7-19 00:50 | 只看该作者
这样有多快呢?

例 15.18. 用 romantest9.py 测试 roman9.py 的结果

.............
----------------------------------------------------------------------
Ran 13 tests in 0.791s

OK

还记得吗?你原有版本的最快速度是 13 个测试耗时 3.315 秒。 当然,这样的比较不完全公平,因为这个新版本需要更长的时间来导入 (当它填充查询表时)。 但是导入只需一次,在运行过程中可以忽略。

这个重构的故事的寓意是什么?
  • 简洁是美德。
  • 特别是使用正则表达式时。
  • 并且单元测试给了你大规模重构的信心...... 既便没写出原有的代码也是这样。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
297#
 楼主| 发表于 2006-7-19 00:50 | 只看该作者
15.5. 小结
单元测试是一个强大的概念,使用得当的话既可以减少维护成本又可以增加长期项目的灵活性。 同样重要的是要意识到单元测试并不是“灵丹妙药”,也不是“银弹”。 编写好的测试用例很困难,保持其更新更需要磨练(特别是当顾客对修复严重的 Bug 大呼小叫之时)。 单元测试不是其它形式测试的替代品,比如说功能性测试、集成测试以及可用性测试。但它切实可行且功效明显,一旦相识,你会反问为什么以往没有应用它。

这一章涵盖了很多内容,有很多都不是 Python 所特有的。 很多语言都有单元测试框架,都要求你理解相同的基本概念:

测试用例的设计方针是目的单一、可以自动运行、互不干扰。
在被测试代码编写 之前 编写测试用例。
编写测试 有效输入的测试用例 并检查正确的结果。
编写测试 无效输入的测试用例 并检查正确的失败。
为 描述 Bug 或 反映新需求 而编写和升级测试用例。
为了改进性能、可伸缩性、可读性、可维护性和任何缺少的特性而无情地重构。
另外,你应该能够自如地做到如下 Python 的特有工作:

继承 unittest.TestCase 生成子类并为每个单独的测试用例编写方法。
使用 assertEqual 检查已知结果的返回。
使用 assertRaises 检查函数是否引发已知异常。
在 if __name__ 子句中调用 unittest.main() 来一次性运行所有测试用例。
以 详细(verbose) 或者 普通(regular) 模式运行单元测试

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
298#
 楼主| 发表于 2006-7-19 00:51 | 只看该作者
进一步阅读

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
299#
 楼主| 发表于 2006-7-19 00:51 | 只看该作者
第 16 章 有效编程(Functional Programming)
16.1. 概览
16.2. 找到路径
16.3. 过滤已访问列表
16.4. 关联已访问列表
16.5. 数据中心思想编程
16.6. 动态导入模块
16.7. 全部放在一起
16.8. 小结
16.1. 概览
在 第 13 章 单元测试 中,你学会了单元测试的哲学。在 第 14 章 以测试优先为原则的编程 中你步入了 Python 基本的单元测试操作,在 第 15 章 重构 部分,你看到单元测试如何另大规模重组变得容易。 本章将在这些程序范例的基础上,集中关注于超越单元测试本身的更高级的 Python 特有技术。

下面是一个作为简单退化(regression)测试框架运行的完整 Python 程序。 它将你前面编写的单独单元测试模块组织在一起成为一个测试套件并一次性运行。实际上这是我构建本书自身代码的一部分。我为几个样例程序都编写了单元测试。(不是只有 第 13 章 单元测试 中的 roman.py 模块),我的自动构建代码的第一个工作便是确保我所有的例子可以正常工作。 如果退化测试程序失败,构建过程当即终止。 我可不想因为发布了不能工作的样例程序而让你在下载他们后坐在显示器前抓耳挠腮地为程序不能运转而烦恼。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
300#
 楼主| 发表于 2006-7-19 00:51 | 只看该作者
例 16.1. regression.py
如果您还没有下载本书附带的例子程序, 可以 下载本程序和其他例子程序。

"""Regression testing framework

This module will search for scripts in the same directory named
XYZtest.py.  Each such script should be a test suite that tests a
module through PyUnit.  (As of Python 2.1, PyUnit is included in
the standard library as "unittest".)  This script will aggregate all
found test suites into one big test suite and run them all at once.
"""

import sys, os, re, unittest

def regressionTest():
    path = os.path.abspath(os.path.dirname(sys.argv[0]))   
    files = os.listdir(path)                              
    test = re.compile("test\.py$", re.IGNORECASE)         
    files = filter(test.search, files)                     
    filenameToModuleName = lambda f: os.path.splitext(f)[0]
    moduleNames = map(filenameToModuleName, files)         
    modules = map(__import__, moduleNames)                 
    load = unittest.defaultTestLoader.loadTestsFromModule  
    return unittest.TestSuite(map(load, modules))         

if __name__ == "__main__":                  
    unittest.main(defaultTest="regressionTest"
把这段代码放在本书其他样例代码相同的目录下运行之,模块 test.py 中的所有单元测试将被找到并一起被运行。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表