企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 附录 B. 五分钟回顾 第 1 章 安装 Python * 1.1. 哪一种 Python 适合您? 学习 Python 的第一件事就是安装,不是吗? * 1.2. Windows 上的 Python 在 Windows 上,安装 Python 有两种选择。 * 1.3. Mac OS X 上的 Python 在 Mac OS X 上,对于安装 Python 有两种选择:安装或不安装。您可能想要安装它。 * 1.4. Mac OS 9 上的 Python Mac OS 9 上没有预装任何版本的 Python,安装相对简单,只有一种选择。 * 1.5. RedHat Linux 上的 Python 在 [http://www.python.org/ftp/python/](http://www.python.org/ftp/python/) 选择列出的最新的版本号, 然后选择 其中的`rpms/` 目录下载最新的 Python RPM 包。 使用 **rpm** 命令进行安装,操作如下所示: * 1.6. Debian GNU/Linux 上的 Python 如果您运行在 Debian GNU/Linux 上,安装 Python 需要使用 **apt** 命令。 * 1.7. 从源代码安装 Python 如果您宁愿从源码创建,可以从 [http://www.python.org/ftp/python/](http://www.python.org/ftp/python/)下载 Python 的源代码。选择最新的版本,下载`.tgz` 文件,执行通常的 **`configure`**, **`make`**, **`make install`** 步骤。 * 1.8. 使用 Python 的交互 Shell 既然我们已经安装了 Python,那么我们运行的这个交互shell是什么东西呢? * 1.9. 小结 您现在应该已经安装了一个可以工作的 Python 版本了。 第 2 章 第一个 Python 程序 * 2.1. 概览 这是一个完整的、可执行的 Python 程序。 * 2.2. 函数声明 与其它大多数语言一样 Python 有函数,但是它没有像 C++ 一样的独立的头文件;或者像 Pascal 一样的分离的 `interface`/`implementation` 段。在需要函数时,像下面这样声明即可: * 2.3. 文档化函数 可以通过给出一个 `doc string` (文档字符串) 来文档化一个 Python 函数。 * 2.4. 万物皆对象 在 Python 中,函数同其它东西一样也是对象。 * 2.5. 代码缩进 Python 函数没有明显的 `begin` 和 `end`,没有标明函数的开始和结束的花括号。唯一的分隔符是一个冒号 (`:`),接着代码本身是缩进的。 * 2.6. 测试模块 所有的 Python 模块都是对象,并且有几个有用的属性。您可以使用这些属性方便地测试您所编写的模块。下面是一个使用 `if` `__name__` 的技巧。 第 3 章 内置数据类型 * 3.1. Dictionary 介绍 Dictionary 是 Python 的内置数据类型之一,它定义了键和值之间一对一的关系。 * 3.2. List 介绍 List 是 Python 中使用最频繁的数据类型。如果您对 list 仅有的经验就是在 Visual Basic 中的数组或 Powerbuilder 中的数据存储,那么就打起精神学习 Python 的 list 吧。 * 3.3. Tuple 介绍 Tuple 是不可变的 list。一旦创建了一个 tuple,就不能以任何方式改变它。 * 3.4. 变量声明 Python 与大多数其它语言一样有局部变量和全局变量之分,但是它没有明显的变量声明。变量通过首次赋值产生,当超出作用范围时自动消亡。 * 3.5. 格式化字符串 Python 支持格式化字符串的输出 。尽管这样可能会用到非常复杂的表达式,但最基本的用法是将一个值插入到一个有字符串格式符 `%s` 的字符串中。 * 3.6. 映射 list Python 的强大特性之一是其对 list 的解析,它提供一种紧凑的方法,可以通过对 list 中的每个元素应用一个函数,从而将一个 list 映射为另一个 list。 * 3.7. 连接 list 与分割字符串 您有了一个形如 `_key_=_value_` 的 key-value 对 list,并且想将它们合成为单个字符串。为了将任意包含字符串的 list 连接成单个字符串,可以使用字符串对象的 `join` 方法。 * 3.8. 小结 现在 `odbchelper.py` 程序和它的输出结果都应该非常清楚了。 第 4 章 自省的威力 * 4.1. 概览 下面是一个完整可运行的 Python 程序。大概看一下这段程序,你应该可以理解不少了。用数字标出的行阐述了 第 2 章 _第一个 Python 程序_ 中涉及的一些概念。如果剩下来的代码看起来有点奇怪,不用担心,通过阅读本章你将会理解所有这些。 * 4.2. 使用可选参数和命名参数 Python 允许函数参数有缺省值;如果调用函数时不使用参数,参数将获得它的缺省值。此外,通过使用命名参数还可以以任意顺序指定参数。SQL Server Transact/SQL 中的存储过程也可以做到这些;如果你是脚本高手,你可以略过这部分。 * 4.3. 使用 type、str、dir 和其它内置函数 Python 有小部分相当有用的内置函数。除这些函数之外,其它所有的函数都被分到了各个模块中。其实这是一个非常明智的设计策略,避免了核心语言变得像其它脚本语言一样臃肿 (咳 咳,Visual Basic)。 * 4.4. 通过 getattr 获取对象引用 你已经知道 Python 函数是对象。你不知道的是,使用 `getattr` 函数,可以得到一个直到运行时才知道名称的函数的引用。 * 4.5. 过滤列表 如你所知,Python 具有通过列表解析 (第 3.6 节 “映射 list”) 将列表映射到其它列表的强大能力。这种能力同过滤机制结合使用,使列表中的有些元素被映射的同时跳过另外一些元素。 * 4.6. and 和 or 的特殊性质 在Python 中,`and` 和 `or` 执行布尔逻辑演算,如你所期待的一样。但是它们并不返回布尔值,而是返回它们实际进行比较的值之一。 * 4.7. 使用 lambda 函数 Python 支持一种有趣的语法,它允许你快速定义单行的最小函数。这些叫做 `lambda` 的函数,是从 Lisp 借用来的,可以用在任何需要函数的地方。 * 4.8. 全部放在一起 最后一行代码是唯一还没有解释过的,它完成全部的工作。但是现在工作已经简单了,因为所需要的每件事都已经按照需求建立好了。所有的多米诺骨牌已经就位,到了将它们推倒的时候了。 * 4.9. 小结 `apihelper.py` 程序和它的输出现在应该非常清晰了。 第 5 章 对象和面向对象 * 5.1. 概览 下面是一个完整的,可运行的 Python 程序。请阅读模块、类和函数的 `doc string`s,可以大概了解这个程序所做的事情和工作情况。像平时一样,不用担心你不理解的东西,这就是本章其它部分将告诉你的内容。 * 5.2. 使用 from module import 导入模块 Python 有两种导入模块的方法。两种都有用,你应该知道什么时候使用哪一种方法。一种方法,`import _module_`,你已经在第 2.4 节 “万物皆对象”看过了。另一种方法完成同样的事情,但是它与第一种有着细微但重要的区别。 * 5.3. 类的定义 Python 是完全面向对象的:你可以定义自已的类,从自已的或内置的类继承,然后从你定义的类创建实例。 * 5.4. 类的实例化 在 Python 中对类进行实例化很直接。要对类进行实例化,只要调用类 (就好像它是一个函数),传入定义在 `__init__` 方法中的参数。返回值将是新创建的对象。 * 5.5. 探索 UserDict:一个封装类 如你所见,`FileInfo` 是一个有着像字典一样的行为方式的类。为了进一步揭示这一点,让我们看一看在 `UserDict` 模块中的 `UserDict` 类,它是我们的 `FileInfo` 类的父类。它没有什么特别的,也是用 Python 写的,并且保存在一个 `.py` 文件里,就像我们其他的代码。特别之处在于,它保存在你的 Python 安装目录的 `lib` 目录下。 * 5.6. 专用类方法 除了普通的类方法,Python 类还可以定义专用方法。专用方法是在特殊情况下或当使用特别语法时由 Python 替你调用的,而不是在代码中直接调用 (像普通的方法那样)。 * 5.7. 高级专用类方法 除了 `__getitem__` 和 `__setitem__` 之外 Python 还有更多的专用函数。某些可以让你模拟出你甚至可能不知道的功能。 * 5.8. 类属性介绍 你已经知道了[数据属性](userdict.html#fileinfo.userdict.init.example "例 5.9. 定义 UserDict 类"),它们是被一个特定的类实例所拥有的变量。Python 也支持类属性,它们是由类本身所拥有的。 * 5.9. 私有函数 与大多数的语言不同,一个 Python 函数,方法,或属性是私有还是公有,完全取决于它的名字。 * 5.10. 小结 实打实的对象把戏到此为止。你将在 第 12 章 中看到一个真实世界应用程序的专有类方法,它使用 `getattr` 创建一个到远程 Web 服务的代理。 第 6 章 异常和文件处理 * 6.1. 异常处理 与许多面向对象语言一样,Python 具有异常处理,通过使用 `try...except` 块来实现。 * 6.2. 与文件对象共事 Python 有一个内置函数,`open`,用来打开在磁盘上的文件。`open` 返回一个文件对象,它拥有一些方法和属性,可以得到被打开文件的信息,以及对被打开文件进行操作。 * 6.3. for 循环 与其它大多数语言一样,Python 也拥有 `for` 循环。你到现在还未曾看到它们的唯一原因就是,Python 在其它太多的方面表现出色,通常你不需要它们。 * 6.4. 使用 sys.modules 与其它任何 Python 的东西一样,模块也是对象。只要导入了,总可以用全局 dictionary ``sys`.modules` 来得到一个模块的引用。 * 6.5. 与目录共事 `os.path` 模块有几个操作文件和目录的函数。这里,我们看看如何操作路径名和列出一个目录的内容。 * 6.6. 全部放在一起 再一次,所有的多米诺骨牌都放好了。我们已经看过每行代码是如何工作的了。现在往回走一步,看一下放在一起是怎么样的。 * 6.7. 小结 在 第 5 章 介绍的 `fileinfo.py` 程序现在应该完全理解了。 第 7 章 正则表达式 * 7.1. 概览 如果你要解决的问题利用字符串函数能够完成,你应该使用它们。它们快速、简单且容易阅读,而快速、简单、可读性强的代码可以说出很多好处。但是,如果你发现你使用了许多不同的字符串函数和 `if` 语句来处理一个特殊情况,或者你组合使用了 `split`、`join` 等函数而导致用一种奇怪的甚至读不下去的方式理解列表,此时,你也许需要转到正则表达式了。 * 7.2. 个案研究:街道地址 这一系列的例子是由我几年前日常工作中的现实问题启发而来的,当时我需要从一个老化系统中导出街道地址,在将它们导入新的系统之前,进行清理和标准化。(看,我不是只将这些东西堆到一起,它有实际的用处。)这个例子展示我如何处理这个问题。 * 7.3. 个案研究:罗马字母 你可能经常看到罗马数字,即使你没有意识到它们。你可能曾经在老电影或者电视中看到它们 (“版权所有 `MCMXLVI`” 而不是 “版权所有`1946`”),或者在某图书馆或某大学的贡献墙上看到它们 (“成立于 `MDCCCLXXXVIII`”而不是“成立于`1888`”)。你也可能在某些文献的大纲或者目录上看到它们。这是一个表示数字的系统,它实际上能够追溯到远古的罗马帝国 (因此而得名)。 * 7.4. 使用 {n,m} 语法 在[前面的章节](roman_numerals.html "7.3. 个案研究:罗马字母"),你处理了相同字符可以重复三次的情况。在正则表达式中,有另外一个方式来表达这种情况,并且能提高代码的可读性。首先看看我们在前面的例子中使用的方法。 * 7.5. 松散正则表达式 迄今为止,你只是处理过被我称之为“紧凑”类型的正则表达式。正如你曾看到的,它们难以阅读,即使你清楚正则表达式的含义,你也不能保证六个月以后你还能理解它。你真正所需的就是利用内联文档 (inline documentation)。 * 7.6. 个案研究:解析电话号码 迄今为止,你主要是匹配整个模式,不论是匹配上,还是没有匹配上。但是正则表达式还有比这更为强大的功能。当一个模式_确实_ 匹配上时,你可以获取模式中特定的片断,你可以发现具体匹配的位置。 * 7.7. 小结 这只是正则表达式能够完成工作的很少一部分。换句话说,即使你现在备受打击,相信我,你也不是什么也没见过了。 第 8 章 HTML 处理 * 8.1. 概览 我经常在 [comp.lang.python](http://groups.google.com/groups?group=comp.lang.python) 上看到关于如下的问题: “ 怎么才能从我的 HTML 文档中列出所有的 [头|图像|链接] 呢?” “怎么才能 [分析|解释|munge] 我的 HTML 文档的文本,但是又要保留标记呢?” “怎么才能一次给我所有的 HTML 标记 [增加|删除|加引号] 属性呢?” 本章将回答所有这些问题。 * 8.2. sgmllib.py 介绍 HTML 处理分成三步:将 HTML 分解成它的组成片段,对片段进行加工,接着将片段再重新合成 HTML。第一步是通过 `sgmllib.py` 来完成的,它是标准 Python 库的一部分。 * 8.3. 从 HTML 文档中提取数据 为了从 HTML 文档中提取数据,将 `SGMLParser` 类进行子类化,然后对想要捕捉的标记或实体定义方法。 * 8.4. BaseHTMLProcessor.py 介绍 `SGMLParser` 自身不会产生任何结果。它只是分析,分析,再分析,对于它找到的有趣的东西会调用相应的一个方法,但是这些方法什么都不做。`SGMLParser` 是一个 HTML _消费者 (consumer)_:它接收 HTML,将其分解成小的、结构化的小块。正如您所看到的,在[前一节](extracting_data.html "8.3. 从 HTML 文档中提取数据")中,您可以定义 `SGMLParser` 的子类,它可以捕捉特别标记和生成有用的东西,如一个网页中所有链接的一个列表。现在我们将沿着这条路更深一步。我们要定义一个可以捕捉 `SGMLParser` 所丢出来的所有东西的一个类,接着重建整个 HTML 文档。用技术术语来说,这个类将是一个 HTML _生产者 (producer)_。 * 8.5. locals 和 globals 我们先偏离一下 HTML 处理的主题,讨论一下 Python 如何处理变量。Python 有两个内置的函数,`locals` 和 `globals`,它们提供了基于 dictionary 的访问局部和全局变量的方式。 * 8.6. 基于 dictionary 的字符串格式化 有另外一种字符串格式化的形式,它使用 dictionary 而不是值的 tuple。 * 8.7. 给属性值加引号 在 [comp.lang.python](http://groups.google.com/groups?group=comp.lang.python) 上的一个常见问题是 “我有一些 HTML 文档,属性值没有用引号括起来,并且我想将它们全部括起来,我怎么才能实现它呢?” \[7\] (一般这种事情的出现是由于一个项目经理加入到一个大的项目中来,而他又抱着 HTML 是一种标记语言的教条,要求所有的页面必须能够通过 HTML 校验器的验证。而属性值没有被引号括起来是一种常见的对 HTML 规范的违反。) 不管什么原因,未括起来的属性值通过将 HTML 送进 `BaseHTMLProcessor` 可以容易地修复。 * 8.8. dialect.py 介绍 `Dialectizer` 是 `BaseHTMLProcessor` 的简单 (和拙劣) 的派生类。它通过一系列的替换对文本块进行了处理,但是它确保在 ``<pre>`...`</pre>`` 块之间的任何东西不被修改地通过。 * 8.9. 全部放在一起 到了将迄今为止我们已经学过并用得不错的东西放在一起的时候了。我希望您专心些。 * 8.10. 小结 Python 向您提供了一个强大工具,`sgmllib.py`,可以通过将 HTML 结构转变为一种对象模型来进行处理。可以以许多不同的方式来使用这个工具。 第 9 章 XML 处理 * 9.1. 概览 处理 XML 有两种基本的方式。一种叫做 SAX (“Simple API for XML”),它的工作方式是,一次读出一点 XML 内容,然后对发现的每一个元素调用一个方法。(如果你读了 第 8 章 _HTML 处理_,这应该听起来很熟悉,因为这是 `sgmllib` 工作的方式。) 另一种方式叫做 DOM (“Document Object Model”),它的工作方式是,一次性读入整个 XML 文档,然后使用 Python 类创建一个内部表示形式 (以树结构进行连接)。Python 拥有这两种解析方式的标准模块,但是本章只涉及 DOM。 * 9.2. 包 实际上解析一个 XML 文档是很简单的:只要一行代码。但是,在你接触那行代码前,需要暂时岔开一下,讨论一下包。 * 9.3. XML 解析 正如我说的,实际解析一个 XML 文档是非常简单的:只要一行代码。从这里出发到哪儿去就是你自己的事了。 * 9.4. Unicode Unicode 是一个系统,用来表示世界上所有不同语言的字符。当 Python 解析一个 XML 文档时,所有的数据都是以unicode的形式保存在内存中的。 * 9.5. 搜索元素 通过一步步访问每一个节点的方式遍历 XML 文档可能很乏味。如果你正在寻找些特别的东西,又恰恰它们深深埋入了你的 XML 文档,有个捷径让你可以快速找到它:`getElementsByTagName` 。 * 9.6. 访问元素属性 XML 元素可以有一个或者多个属性,只要已经解析了一个 XML 文档,访问它们就太简单了。 * 9.7. Segue 以上就是 XML 的核心内容。下一章将使用相同的示例程序,但是焦点在于能使程序更加灵活的其它方面:使用输入流处理,使用 `getattr` 进行方法分发,并使用命令行标识允许用户重新配置程序而无需修改代码。 第 10 章 脚本和流 * 10.1. 抽象输入源 Python 的最强大力量之一是它的动态绑定,而动态绑定最强大的用法之一是_类文件(file-like)对象_。 * 10.2. 标准输入、输出和错误 UNIX 用户已经对标准输入、标准输出和标准错误的概念非常熟悉了。这一节是为其他不熟悉的人准备的。 * 10.3. 查询缓冲节点 `kgp.py` 使用了多种技巧,在你进行 XML 处理时,它们或许能派上用场。第一个就是,利用输入文档的结构稳定特征来构建节点缓冲。 * 10.4. 查找节点的直接子节点 解析 XML 文档时,另一个有用的己技巧是查找某个特定元素的所有直接子元素。例如,在语法文件中,一个 `ref` 元素可以有数个 `p` 元素,其中每一个都可以包含很多东西,包括其他的 `p` 元素。你只要查找作为 `ref` 孩子的 `p` 元素,不用查找其他 `p` 元素的孩子 `p` 元素。 * 10.5. 根据节点类型创建不同的处理器 第三个有用的 XML 处理技巧是将你的代码基于节点类型和元素名称分散到逻辑函数中。解析后的 XML 文档是由各种类型的节点组成的,每一个都是通过 Python 对象表示的。文档本身的根层次通过一个 `Document` 对象表示。`Document` 还包含了一个或多个 `Element` 对象 (表示 XML 标记),其中的每一个可以包含其它的 `Element` 对象、`Text` 对象 (表示文本),或者 `Comment` 对象 (表示内嵌注释)。使用 Python 编写分离各个节点类型逻辑的分发器非常容易。 * 10.6. 处理命令行参数 Python 完全支持创建在命令行运行的程序,也支持通过命令行参数和短长样式来指定各种选项。这些并非是 XML 特定的,但是这样的脚本可以充分使用命令行处理,看来是时候提一下它了。 * 10.7. 全部放在一起 你已经了解很多基础的东西。让我们回来看看所有片段是如何整合到一起的。 * 10.8. 小结 Python 带有解析和操作 XML 文档非常强大的库。`minidom` 接收一个 XML 文件并将其解析为 Python 对象,并提供了对任意元素的随机访问。进一步,本章展示了如何利用 Python 创建一个“真实”独立的命令行脚本,连同命令行标志、命令行参数、错误处理,甚至从前一个程序的管道接收输入的能力。 第 11 章 HTTP Web 服务 * 11.1. 概览 在讲解如何下载 web 页和如何从 URL 解析 XML时,你已经学习了关于 HTML 处理和 XML 处理,接下来让我们来更全面地探讨有关 HTTP web 服务的主题。 * 11.2. 避免通过 HTTP 重复地获取数据 假如说你想用 HTTP 下载资源,例如一个 Atom feed 汇聚。你不仅仅想下载一次;而是想一次又一次地下载它,如每小时一次,从提供 news feed 的站点获得最新的消息。让我们首先用一种直接而原始的方法来实现它,然后看看如何改进它。 * 11.3. HTTP 的特性 这里有五个你必须关注的 HTTP 重要特性。 * 11.4. 调试 HTTP web 服务 首先,让我们开启 Python HTTP 库的调试特性并查看网络线路上的传输过程。这对本章的全部内容都很有用,因为你将添加越来越多的特性。 * 11.5. 设置 User-Agent 改善你的 HTTP web 服务客户端的第一步就是用 `User-Agent` 适当地鉴别你自己。为了做到这一点,你需要远离基本的 `urllib` 而深入到 `urllib2`。 * 11.6. 处理 Last-Modified 和 ETag 既然你知道如何在你的 web 服务请求中添加自定义的 HTTP 头信息,接下来看看如何添加 `Last-Modified` 和 `ETag` 头信息的支持。 * 11.7. 处理重定向 你可以使用两种不同的自定义 URL 处理器来处理永久重定向和临时重定向。 * 11.8. 处理压缩数据 你要支持的最后一个重要的 HTTP 特性是压缩。许多 web 服务具有发送压缩数据的能力,这可以将网络线路上传输的大量数据消减 60% 以上。这尤其适用于 XML web 服务,因为 XML 数据 的压缩率可以很高。 * 11.9. 全部放在一起 你已经看到了构造一个智能的 HTTP web 客户端的所有片断。现在让我们看看如何将它们整合到一起。 * 11.10. 小结 `openanything.py` 及其函数现在可以完美地工作了。 第 12 章 SOAP Web 服务 * 12.1. 概览 你用 Google,对吧?它是一个很流行的搜索引擎。你是否希望能以程序化的方式访问 Google 的搜索结果呢?现在你能做到了。下面是一个用 Python 搜索 Google 的程序。 * 12.2. 安装 SOAP 库 与本书中的其他代码不同,本章依赖的库不是 Python 预安装的。 * 12.3. 步入 SOAP 调用远程函数是 SOAP 的核心功能。有很多提供公开 SOAP 访问的服务器提供用于展示的简单功能。 * 12.4.  SOAP 网络服务查错 SOAP 提供了一个很方便的方法用以查看背后的情形。 * 12.5. WSDL 介绍 `SOAPProxy` 类本地方法调用并透明地转向到远程 SOAP 方法。正如你所看到的,这是很多的工作,`SOAPProxy` 快速和透明地完成他们。它没有做到的是提供方法自省的手段。 * 12.6. 以 WSDL 进行 SOAP 内省 就像网络服务舞台上的所有事物,WSDL 也经历了一个充满明争暗斗而且漫长多变的历史。我不打算讲述这段令我伤心的历史。还有一些其他的标准提供相同的支持,但 WSDL 还是胜出,所以我们还是来学习一下如何使用它。 * 12.7. 搜索 Google 让我们回到这章开始时你看到的那段代码,获得比当前气温更有价值和令人振奋的信息。 * 12.8.  SOAP 网络服务故障排除 是的,SOAP 网络服务的世界中也不总是欢乐和阳光。有时候也会有故障。 * 12.9. 小结 SOAP 网络服务是很复杂的,雄心勃勃的它试图涵盖网络服务的很多不同应用。这一章我们接触了它的一个简单应用。 第 13 章 单元测试 * 13.1. 罗马数字程序介绍 II 在前面的章节中,通过阅读代码,你迅速“深入”,以最快的速度理解了各个程序。既然你已对 Python 有了一定的了解,那么接下来让我们看看程序开发_之前_ 的工作。 * 13.2. 深入 现在你已经定义了你的转换程序所应有的功能,下面一步会有点儿出乎你的意料:你将要开发一个测试组件 (test suite) 来测试你未来的函数以确保它们工作正常。没错:你将为还未开发的程序开发测试代码。 * 13.3. romantest.py 介绍 这是将被开发并保存为 `roman.py` 的罗马数字转换程序的完整测试组件 (test suite)。很难立刻看出它们是如何协同工作的,似乎所有类或者方法之间都没有关系。这是有原因的,而且你很快就会明了。 * 13.4. 正面测试 (Testing for success) 单元测试的基础是构建独立的测试用例 (test case)。一个测试用例只回答一个关于被测试代码的问题。 * 13.5. 负面测试 (Testing for failure) 使用有效输入确保函数成功通过测试还不够,你还需要测试无效输入导致函数失败的情形。但并不是任何失败都可以,必须如你预期地失败。 * 13.6. 完备性检测 (Testing for sanity) 你经常会发现一组代码中包含互逆的转换函数,一个把 A 转换为 B ,另一个把 B 转换为 A。在这种情况下,创建“完备性检测”可以使你在由 A 转 B 再转 A 的过程中不会出现丢失精度或取整等错误。 第 14 章 测试优先编程 * 14.1. roman.py, 第 1 阶段 到目前为止,单元测试已经完成,是时候开始编写被单元测试测试的代码了。你将分阶段地完成这个工作,因此开始时所有的单元测试都是失败的,但在逐步完成 `roman.py` 的同时你会看到它们一个个地通过测试。 * 14.2. roman.py, 第 2 阶段 现在你有了 `roman` 模块的大概框架,到了开始写代码以通过测试的时候了。 * 14.3. roman.py, 第 3 阶段 现在 `toRoman` 对于有效的输入 (`1` 到 `3999` 整数) 已能正确工作,是正确处理那些无效输入 (任何其他输入) 的时候了。 * 14.4. roman.py, 第 4 阶段 现在 `toRoman` 完成了,是开始编写 `fromRoman` 的时候了。感谢那个将每个罗马数字和对应整数关连的完美数据结构,这个工作不比 `toRoman` 函数复杂。 * 14.5. roman.py, 第 5 阶段 现在 `fromRoman` 对于有效输入能够正常工作了,是揭开最后一个谜底的时候了:使它正常工作于无效输入的情况下。这意味着要找出一个方法检查一个字符串是不是有效的罗马数字。这比 `toRoman` 中[验证有效的数字输入](stage_3.html "14.3. roman.py, 第 3 阶段")困难,但是你可以使用一个强大的工具:正则表达式。 第 15 章 重构 * 15.1. 处理 bugs 尽管你很努力地编写全面的单元测试,但是 bug 还是会出现。我所说的 “bug” 是什么呢?Bug 是你还没有编写的测试用例。 * 15.2. 应对需求变化 尽管你竭尽努力地分析你的客户,并点灯熬油地提炼出精确的需求,但需求还是会是不断变化。大部分客户在看到产品前不知道他们想要什么。即便知道,也不擅于精确表述出他们的有效需求。即便能表述出来,他们在下一个版本一定会要求更多的功能。因此你需要做好更新测试用例的准备以应对需求的改变。 * 15.3. 重构 全面的单元测试带来的最大好处不是你的全部测试用例最终通过时的成就感;也不是被责怪破坏了别人的代码时能够_证明_ 自己的自信。最大的好处是单元测试给了你自由去无情地重构。 * 15.4. 后记 聪明的读者在学习[前一节](refactoring.html "15.3. 重构")时想得会更深入一层。现在写的这个程序中最令人头痛的性能负担是正则表达式,但它是必需的,因为没有其它方法来识别罗马数字。但是,它们只有 5000 个,为什么不一次性地构建一个查询表来读取?不必用正则表达式凸现了这个主意的好处。你建立了整数到罗马数字查询表的时候,罗马数字到整数的逆向查询表也构建了。 * 15.5. 小结 单元测试是一个强大的概念,使用得当的话既可以减少维护成本又可以增加长期项目的灵活性。同样重要的是要意识到单元测试并不是“灵丹妙药”,也不是“银弹”。编写好的测试用例很困难,保持其更新更需要磨练 (特别是当顾客对修复严重的 Bug 大呼小叫之时)。单元测试不是其它形式测试的替代品,比如说功能性测试、集成测试以及可用性测试。但它切实可行且功效明显,一旦相识,你会反问为什么以往没有应用它。 第 16 章 函数编程 * 16.1. 概览 在 第 13 章 _单元测试_ 中,你学会了单元测试的哲学。在 第 14 章 _测试优先编程_ 中你步入了 Python 基本的单元测试操作,在 第 15 章 _重构_ 部分,你看到单元测试如何令大规模重构变得容易。本章将在这些程序样例的基础上,集中关注于超越单元测试本身的更高级的 Python 特有技术。 * 16.2. 找到路径 从命令行运行 Python 代码时,知道所运行代码在磁盘上的存储位置有时候是有必要的。 * 16.3. 重识列表过滤 你已经熟识了应用列表解析来过滤列表。这里介绍的是达到相同效果的另一种令很多人感觉清晰的实现方法。 * 16.4. 重识列表映射 你对使用列表解析映射列表的做法已经熟知。另一种方法可以完成同样的工作:使用内建 `map` 函数。它的工作机理和 [`filter`](filtering_lists.html "16.3. 重识列表过滤") 函数类似。 * 16.5. 数据中心思想编程 现在的你,可能正抓耳挠腮地狠想,为什么这样比使用 `for` 循环和直接调用函数好。这是一个非常好的问题。通常这是一个程序观问题。使用 `map` 和 `filter` 强迫你围绕数据进行思考。 * 16.6. 动态导入模块 好了,大道理谈够了。让我们谈谈动态导入模块吧。 * 16.7. 全部放在一起 你已经学习了足够的知识,现在来分析本章样例代码的前七行:读取一个目录并从中导入选定的模块。 * 16.8. 小结 `regression.py` 程序及其输出到现在应该很清楚了。 第 17 章 动态函数 * 17.1. 概览 我想谈谈名词复数。还有,返回其它函数的函数,高级的正则表达式和生成器 (Generator)。生成器是 Python 2.3 新引入的。但首先还是让我们先来谈谈如何生成名词复数。 * 17.2. plural.py, 第 1 阶段 你所针对的单词 (至少在英语中) 是字符串和字符。你还需要规则来找出不同的字符 (字母) 组合,并对它们进行不同的操作。这听起来像是正则表达式的工作。 * 17.3. plural.py, 第 2 阶段 现在你将增加一个抽象过程。你从定义一个规则列表开始:如果这样,就做那个,否则判断下一规则。让我们暂时将程序一部分复杂化以便使另一部分简单化。 * 17.4. plural.py, 第 3 阶段 将每个匹配和规则应用分别制作成函数没有必要。你从来不会直接调用它们:你把它们定义于 `rules` 列表之中并从那里调用它们。让我们隐去它们的函数名而抓住规则定义的主线。 * 17.5. plural.py, 第 4 阶段 让我们精炼出代码中的重复之处,以便更容易地定义新规则。 * 17.6. plural.py, 第 5 阶段 你已经精炼了所有重复代码,也尽可能地把复数规则提炼到定义一个字符串列表。接下来的步骤是把这些字符串提出来放在另外的文件中,从而可以和使用它们的代码分开来维护。 * 17.7. plural.py, 第 6 阶段 现在你已准备好探讨生成器 (Generator) 了。 * 17.8. 小结 这一章中我们探讨了几个不同的高级技术。它们并不都适用于任何情况。 第 18 章 性能优化 * 18.1. 概览 由于代码优化过程中存在太多的不明确因素,以至于你很难清楚该从何入手。 * 18.2. 使用 timeit 模块 关于 Python 代码优化你需要知道的最重要问题是,决不要自己编写计时函数。 * 18.3. 优化正则表达式 Soundex 函数的第一件事是检查输入是否是一个空字符串。怎样做是最好的方法? * 18.4. 优化字典查找 Soundex 算法的第二步是依照特定规则将字符转换为数字。做到这点最好的方法是什么? * 18.5. 优化列表操作 Soundex 算法的第三步是去除连续重复字符。怎样做是最佳方法? * 18.6. 优化字符串操作 Soundex 算法的最后一步是对短结果补零和截短长结果。最佳的做法是什么? * 18.7. 小结 这一章展示了性能优化的几个重要方面,这里是就 Python 而言,但它们却普遍适用。