楼主: 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
311#
 楼主| 发表于 2006-7-19 00:53 | 只看该作者
例 16.12. regression.py 中的 map
    filenameToModuleName = lambda f: os.path.splitext(f)[0]
    moduleNames = map(filenameToModuleName, files)            正如你在 第 4.7 节 “使用 lambda 函数” 中所见, lambda 定义一个内嵌函数。 也正如你在 例 6.17 “分割路径名” 中所见, os.path.splitext 接受一个文件名并返回一个元组 (name, extension)。因此 filenameToModuleName 是一个接受文件名并提出文件扩展名而只返回文件名称的函数。
  调用它 map 接受files列出的所有文件名,把它传递给 filenameToModuleName 函数,并且返回每个函数调用结果所组成的列表。 换句话说,你剔除掉文件名的扩展名,并将剔除后的文件名存于 moduleNames 之中。

如你在本章剩余部分将看到的,你可以将这种数据中心思想扩展应用到定义和执行一个容纳来自很多单个测试套件的测试的一个测试套件的最终目标。

Footnotes
[8] 同前,我需要指出 map 可以接受一个列标、元组,或者一个像序列一样的对象。参见前面的关于 filter 的脚注。

使用道具 举报

回复
论坛徽章:
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
312#
 楼主| 发表于 2006-7-19 00:54 | 只看该作者
16.5. 数据中心思想编程
现在的你,可能正抓耳挠腮地狠想,为什么这样比使用 for 循环和直接调用函数好。这是一个非常好的问题。通常这是一个程序观问题。 使用 map 和 filter 强迫你围绕数据进行思考。

就此而言,你从没有数据开始,你所做的第一件事是 获得当前脚本的目录路径,并获得该目录中的文件列表。 这就是关键的一步,使你有了被处理的真实数据:文件名列表。

当然,你知道你并不关心所有的文件,而只关心测试套件。你有 太多数据, 因此你需要 filter 数据。 你如何知道哪些数据应该保留? 你需要一个测试来确定,因此你定义一个测试并把它传给 filter 函数。 这里你应用了一个正则表达式来确定,但无论如何构建测试,原则是一样的。

现在你有了每个测试套件的文件名(且局限于测试套件,因为所有其他内容都被过滤掉了),但是你确实还需要以模块名来替代之。 你有正确数量的数据,只是 格式不正确。 因此,你定义了一个函数来将文件名转换为模块名,并把这个函数关联到整个列表。 从一个文件名,你可以获得一个模块名,从一个文件名列表,你可以获得一个模块名列表。

如果不应用 filter, 你也可以使用 for 循环结合一个 if 语句的方法。 map 的使用则可以由一个 for 循环和一个函数调用来取代。 但是 for 循环看起来像是个繁重的工作。至少,简单讲是在浪费时间,糟糕的话还会隐埋错误(Bug)。 比方说,你需要弄清楚如何测试这样一个条件“这个文件是测试套件吗?” 不管怎么说,这是应用细化逻辑,没有哪个语言可以让我们这样做。 但是一旦你搞清楚了,你还需要费尽周折的定义一个新的空列表、写一个 for 循环以及一个 if 语句并手工地调用 append 将符合条件的元素一个个添加到新列表中,然后一路注意区分那个变量里放着过滤后的数据,那个变量里放着未过滤得老数据吗? 为什么不直接定义测试条件,然后由 Python 为你完成接下来的工作呢?

当然啦,你可以尝试眩一点的做法,去删除列表中的元素而不新建一个列表。 但是你以前吃过这样的亏。 试图在循环中改变数据结构是很容易出问题的。 Python 是一个这样工作的语言吗? 用多长时间你才能搞清这一点? 你能确定记得你第二次这样尝试的安全性? 程序员在和这类纯技术课题较劲的过程中,花费了太多的时间、犯了太多的错误,却并没有什么意义。这样并不可能令你的程序有所进步,只不过是费力不讨好。

我在第一次学习 Python 时是抵触列表遍历的, 而且我抗拒 filter 和 map 的时间更长。 我坚持着我更艰难的生活,固守着类似于 for 循环和 if 语句以及一步步地代码编程方式。 而且我的 Python 程序看起来很像是 Visual Basic 程序,细化每一个函数中的每一个操作步骤。 它们却有着同样的小错误和隐蔽的 Bug。 这一切其实都没有意义。

让这一切都远去吧。 费力不讨好的编程不重要,数据重要。 并且数据并不难,他们不过就是数据。 如果多了,就过滤。 如果不是我们要的,就关联。 聚焦在数据上,摒弃费力的劳作。

使用道具 举报

回复
论坛徽章:
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
313#
 楼主| 发表于 2006-7-19 00:54 | 只看该作者
16.6. 动态导入模块
好了,大道理谈够了。让我们谈谈动态倒入数据吧。

首先,让我们看一看正常的导入模块。 import module 语法查看搜索路径寻找已命名模块并以名字导入它们。你甚至于可以以这种方法,以逗号分割同时导入多个模块,本章代码前几行就是这样做的。

例 16.13. 同时导入多个模块

import sys, os, re, unittest
  这里同时导入四个模块:sys (为系统函数和得到命令行参数), os (为目录列表之类的操作系统函数), re (为正则表达式),以及 unittest (为单元测试)。

现在让我们用动态导入做同样的事。

例 16.14. 动态倒入模块
>>> sys = __import__('sys')           
>>> os = __import__('os')
>>> re = __import__('re')
>>> unittest = __import__('unittest')
>>> sys                              
>>> <module 'sys' (built-in)>
>>> os
>>> <module 'os' from '/usr/local/lib/python2.2/os.pyc'>
  内建 __import__ 函数与 import 语句的既定目标相同,但它是一个真正的函数,并接受一个字符串参数。
  变量 sys 现在是 sys 模块, 就像你所说的 import sys。 变量 os 现在是一个 os 模块,等等。

因此 __import__ 导入一个模块,但是是通过一个字符串参数来做到的。 依此处讲,你导入的仅仅是一个硬性的字符串代码,但它可以是一个简单的变量,或者一个函数调用的结果。 并且你指向模块的变量也不必与模块名匹配。 你可以导入一系列模块并把它们指派给一个列表。

使用道具 举报

回复
论坛徽章:
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
314#
 楼主| 发表于 2006-7-19 00:54 | 只看该作者
例 16.15. 动态导入一个列表锁定的模块
>>> moduleNames = ['sys', 'os', 're', 'unittest']
>>> moduleNames
['sys', 'os', 're', 'unittest']
>>> modules = map(__import__, moduleNames)        
>>> modules                                       
[<module 'sys' (built-in)>,
<module 'os' from 'c:\Python22\lib\os.pyc'>,
<module 're' from 'c:\Python22\lib\re.pyc'>,
<module 'unittest' from 'c:\Python22\lib\unittest.pyc'>]
>>> modules[0].version                           
'2.2.2 (#37, Nov 26 2002, 10:24:37) [MSC 32 bit (Intel)]'
>>> import sys
>>> sys.version
'2.2.2 (#37, Nov 26 2002, 10:24:37) [MSC 32 bit (Intel)]'
  moduleNames 只是一个字符串列表。 没什么特别的,只是这些名字刚好是你可应需而用的可导入模块名。
  令人惊奇,你需要导入他们,且通过关联 __import__ 到列表实现了。记住,列表(moduleNames)的每个元素将被用来一次次调用函数 (__import__)并以一个返回值构成的列表作为返回结果。
  所以现在你已经由一个字符串列表构建起了一个实际模块的列表。(你的路径可能不同,这取决于你的操作系统,你安装 Python 的位置,月亮残缺的程度等等)
  从这些是真实模块这一点出发,让我们来看一些模块属性。 记住, modules[0] 是 sys 模块, 因此, modules[0].version 是 sys.version。 所有模块的其他属性和方法也都可用。 import 语句没什么神奇的, 模块也没什么神奇的。 模块就是对象,一切都是对象。

现在,你应该能够把这一切放在一起并能搞清楚本章大部分范例代码的是做什么的。

使用道具 举报

回复
论坛徽章:
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
315#
 楼主| 发表于 2006-7-19 00:54 | 只看该作者
16.7. 全部放在一起
你已经学习了足够的知识,现在来分析本章范例代码的前七行:读取一个目录并从中导入选定的模块。

例 16.16. regressionTest 函数

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))         
让我们一行行交互地看。 假定当前目录是 c:\diveintopython\py,其中有包含本章脚本在内的本书众多范例。 正如在 第 16.2 节 “找到路径” 中所见, 脚本目录将最终存于 path 变量, 因此让我们从这里开始以实打实的代码起步。

使用道具 举报

回复
论坛徽章:
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
316#
 楼主| 发表于 2006-7-19 00:54 | 只看该作者
例 16.17. 步骤 1: 获得所有文件
>>> import sys, os, re, unittest
>>> path = r'c:\diveintopython\py'
>>> files = os.listdir(path)                              
>>> files
['BaseHTMLProcessor.py', 'LICENSE.txt', 'apihelper.py', 'apihelpertest.py',
'argecho.py', 'autosize.py', 'builddialectexamples.py', 'dialect.py',
'fileinfo.py', 'fullpath.py', 'kgptest.py', 'makerealworddoc.py',
'odbchelper.py', 'odbchelpertest.py', 'parsephone.py', 'piglatin.py',
'plural.py', 'pluraltest.py', 'pyfontify.py', 'regression.py', 'roman.py', 'romantest.py',
'uncurly.py', 'unicode2koi8r.py', 'urllister.py', 'kgp', 'plural', 'roman',
'colorize.py']
  files 是由脚本所在目录的所有文件和目录构成的列表。 (如果你已经运行了其中的一些范例,可能还会看到一些 .pyc 文件。)

使用道具 举报

回复
论坛徽章:
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
317#
 楼主| 发表于 2006-7-19 00:55 | 只看该作者
例 16.18. 步骤 2: 找到你关注的多个文件
>>> test = re.compile("test\.py$", re.IGNORECASE)           
>>> files = filter(test.search, files)                     
>>> files                                                   
['apihelpertest.py', 'kgptest.py', 'odbchelpertest.py', 'pluraltest.py', 'romantest.py']
  这个正则表达式将匹配以 test.py 结尾的任意字符串。 注意,你必须转义这个点号,因为正则表达式中的点号通常意味着 “匹配任意单字符”, 但是你实际上想匹配的事一个真正的点号。
  被编译的正则表达式就像一个函数,因此你可以用它来过滤文件和目录构成的大列表,找寻符合正则表达式的所有元素。
  剩下的是一个单元测试脚本列表,因为只有它们是 SOMETHINGtest.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
318#
 楼主| 发表于 2006-7-19 00:55 | 只看该作者
例 16.19. 步骤 3: 文件名和模块名关联
>>> filenameToModuleName = lambda f: os.path.splitext(f)[0]
>>> filenameToModuleName('romantest.py')                    
'romantest'
>>> filenameToModuleName('odchelpertest.py')
'odbchelpertest'
>>> moduleNames = map(filenameToModuleName, files)         
>>> moduleNames                                             
['apihelpertest', 'kgptest', 'odbchelpertest', 'pluraltest', 'romantest']
  正如你在 第 4.7 节 “使用 lambda 函数” 中所见, lambda 快餐式创建内嵌单行函数。 这里应用你在 例 6.17 “分割路径名” 中已经见过的,标准库的 os.path.splitext 将一个带有扩展名的文件名返回成只包含文件名称部分。
  filenameToModuleName 是一个函数。 lambda 与你以 def 语句定义的针对正则表达式的函数相比并不神奇。 你可以如其他函数一样的调用 filenameToModuleName ,它也将如你所愿:从参数中剔除扩展名。
  现在你可以通过 map 把这个函数应用于单元测试文件列表中的每一个文件。
  结果当然如你所愿:以指代模块的字符串构成的一个列表。

使用道具 举报

回复
论坛徽章:
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
319#
 楼主| 发表于 2006-7-19 00:55 | 只看该作者
例 16.20. 步骤 4: 模块名和模块关联
>>> modules = map(__import__, moduleNames)                  
>>> modules                                                
[<module 'apihelpertest' from 'apihelpertest.py'>,
<module 'kgptest' from 'kgptest.py'>,
<module 'odbchelpertest' from 'odbchelpertest.py'>,
<module 'pluraltest' from 'pluraltest.py'>,
<module 'romantest' from 'romantest.py'>]
>>> modules[-1]                                             
<module 'romantest' from 'romantest.py'>
  正如你在 第 16.6 节 “动态导入模块” 中所见,你可以通过 map 和 __import__ 的协同工作,将模块名(字符串)关联到实际的模块(向其他模块一样可以被调用和使用)。  
  modules 现在是一个模块列表,向其他模块一样可用。
  该列表的最后一个模块 是 romantest 模块,恰是你曾说的 import romantest 所指的模块。

使用道具 举报

回复
论坛徽章:
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
320#
 楼主| 发表于 2006-7-19 00:55 | 只看该作者
例 16.21. 步骤 5: 将模块调入测试套件
>>> load = unittest.defaultTestLoader.loadTestsFromModule  
>>> map(load, modules)                     
[<unittest.TestSuite tests=[
  <unittest.TestSuite tests=[<apihelpertest.BadInput testMethod=testNoObject>]>,
  <unittest.TestSuite tests=[<apihelpertest.KnownValues testMethod=testApiHelper>]>,
  <unittest.TestSuite tests=[
    <apihelpertest.ParamChecks testMethod=testCollapse>,
    <apihelpertest.ParamChecks testMethod=testSpacing>]>,
    ...
  ]
]
>>> unittest.TestSuite(map(load, modules))
  模块对象的存在,使你不但可以像其他模块一样地使用它们;通过类的实例化和函数的调用,你还可以内省模块,从而弄清楚已经有了那些类和函数。 这正是 loadTestsFromModule 方法的工作:内省每一个模块并为每个模块返回一个 unittest.TestSuite 对象。 每个 TestSuite 对象实际上都包含了一个 TestSuite 对象列表,每个对象对应着你的模块中的一个测试方法。
  最后,你将 测试套件(TestSuite) 列表封装成一个大的测试套件。unittest 模块会很自如地遍历嵌套于测试套件中的树状结构,最后深入到独立测试方法,一个个加以运行并判断通过或是失败。

自省过程是 unittest 模块经常为我们做的一项工作。 还记得我们的独立测试模块调用并大刀阔斧地完成一起工作的那个看似神奇的 unittest.main()函数吗? unittest.main() 实际上创建了一个 unittest.TestProgram 的实例,而这个实例实际上创建了一个 unittest.defaultTestLoader 的实例并以调用它的模块启动它。 (如果你不给出,如何知道调用它的模块是哪一个?通过使用同样神奇的 __import__('__main__') 命令,动态导入正在运行的模块。 我可以就 unittest 模块中使用的所有技巧和技术写一本书,但那样我就没法写完这本了。)

例 16.22. 步骤 6: 告知 unittest 使用你的测试套件

if __name__ == "__main__":                  
    unittest.main(defaultTest="regressionTest"
  在不使用 unittest 模块来为我们做这一切的神奇工作的情况下,你实际上已自己做到了。 你已经创建了一个自己就能导入模块、调用 unittest.defaultTestLoader 并封装于一个测试套件的(regressionTest) 函数。现在你所要做的不是去寻找测试并以通用的方法构建一个测试套件,而是告诉 unittest 前面那些,它将调用返回可以直接使用的 TestSuite 的 regressionTest 函数。

使用道具 举报

回复

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

本版积分规则 发表回复

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