# 附錄?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 而言,但它們卻普遍適用。
- 版權信息
- 第?1?章?安裝 Python
- 1.1.?哪一種 Python 適合您?
- 1.2.?Windows 上的 Python
- 1.3.?Mac OS X 上的 Python
- 1.4.?Mac OS 9 上的 Python
- 1.5.?RedHat Linux 上的 Python
- 1.6.?Debian GNU/Linux 上的 Python
- 1.7.?從源代碼安裝 Python
- 1.8.?使用 Python 的交互 Shell
- 1.9.?小結
- 第?2?章?第一個 Python 程序
- 2.1.?概覽
- 2.2.?函數聲明
- 2.3.?文檔化函數
- 2.4.?萬物皆對象
- 2.5.?代碼縮進
- 2.6.?測試模塊
- 第?3?章?內置數據類型
- 3.1.?Dictionary 介紹
- 3.2.?List 介紹
- 3.3.?Tuple 介紹
- 3.4.?變量聲明
- 3.5.?格式化字符串
- 3.6.?映射 list
- 3.7.?連接 list 與分割字符串
- 3.8.?小結
- 第?4?章?自省的威力
- 4.1.?概覽
- 4.2.?使用可選參數和命名參數
- 4.3.?使用 type、str、dir 和其它內置函數
- 4.4.?通過 getattr 獲取對象引用
- 4.5.?過濾列表
- 4.6.?and 和 or 的特殊性質
- 4.7.?使用 lambda 函數
- 4.8.?全部放在一起
- 4.9.?小結
- 第?5?章?對象和面向對象
- 5.1.?概覽
- 5.2.?使用 from _module_ import 導入模塊
- 5.3.?類的定義
- 5.4.?類的實例化
- 5.5.?探索 UserDict:一個封裝類
- 5.6.?專用類方法
- 5.7.?高級專用類方法
- 5.8.?類屬性介紹
- 5.9.?私有函數
- 5.10.?小結
- 第?6?章?異常和文件處理
- 6.1.?異常處理
- 6.2.?與文件對象共事
- 6.3.?for 循環
- 6.4.?使用 `sys.modules`
- 6.5.?與目錄共事
- 6.6.?全部放在一起
- 6.7.?小結
- 第?7?章?正則表達式
- 7.1.?概覽
- 7.2.?個案研究:街道地址
- 7.3.?個案研究:羅馬字母
- 7.4.?使用 {n,m} 語法
- 7.5.?松散正則表達式
- 7.6.?個案研究:解析電話號碼
- 7.7.?小結
- 第?8?章?HTML 處理
- 8.1.?概覽
- 8.2.?sgmllib.py 介紹
- 8.3.?從 HTML 文檔中提取數據
- 8.4.?BaseHTMLProcessor.py 介紹
- 8.5.?locals 和 globals
- 8.6.?基于 dictionary 的字符串格式化
- 8.7.?給屬性值加引號
- 8.8.?dialect.py 介紹
- 8.9.?全部放在一起
- 8.10.?小結
- 第?9?章?XML 處理
- 9.1.?概覽
- 9.2.?包
- 9.3.?XML 解析
- 9.4.?Unicode
- 9.5.?搜索元素
- 9.6.?訪問元素屬性
- 9.7.?Segue [9]
- 第?10?章?腳本和流
- 10.1.?抽象輸入源
- 10.2.?標準輸入、輸出和錯誤
- 10.3.?查詢緩沖節點
- 10.4.?查找節點的直接子節點
- 10.5.?根據節點類型創建不同的處理器
- 10.6.?處理命令行參數
- 10.7.?全部放在一起
- 10.8.?小結
- 第?11?章?HTTP Web 服務
- 11.1.?概覽
- 11.2.?避免通過 HTTP 重復地獲取數據
- 11.3.?HTTP 的特性
- 11.4.?調試 HTTP web 服務
- 11.5.?設置 User-Agent
- 11.6.?處理 Last-Modified 和 ETag
- 11.7.?處理重定向
- 11.8.?處理壓縮數據
- 11.9.?全部放在一起
- 11.10.?小結
- 第?12?章?SOAP Web 服務
- 12.1.?概覽
- 12.2.?安裝 SOAP 庫
- 12.3.?步入 SOAP
- 12.4.? SOAP 網絡服務查錯
- 12.5.?WSDL 介紹
- 12.6.?以 WSDL 進行 SOAP 內省
- 12.7.?搜索 Google
- 12.8.? SOAP 網絡服務故障排除
- 12.9.?小結
- 第?13?章?單元測試
- 13.1.?羅馬數字程序介紹 II
- 13.2.?深入
- 13.3.?romantest.py 介紹
- 13.4.?正面測試 (Testing for success)
- 13.5.?負面測試 (Testing for failure)
- 13.6.?完備性檢測 (Testing for sanity)
- 第?14?章?測試優先編程
- 14.1.?roman.py, 第 1 階段
- 14.2.?roman.py, 第 2 階段
- 14.3.?roman.py, 第 3 階段
- 14.4.?roman.py, 第 4 階段
- 14.5.?roman.py, 第 5 階段
- 第?15?章?重構
- 15.1.?處理 bugs
- 15.2.?應對需求變化
- 15.3.?重構
- 15.4.?后記
- 15.5.?小結
- 第?16?章?函數編程
- 16.1.?概覽
- 16.2.?找到路徑
- 16.3.?重識列表過濾
- 16.4.?重識列表映射
- 16.5.?數據中心思想編程
- 16.6.?動態導入模塊
- 16.7.?全部放在一起
- 16.8.?小結
- 第?17?章?動態函數
- 17.1.?概覽
- 17.2.?plural.py, 第 1 階段
- 17.3.?plural.py, 第 2 階段
- 17.4.?plural.py, 第 3 階段
- 17.5.?plural.py, 第 4 階段
- 17.6.?plural.py, 第 5 階段
- 17.7.?plural.py, 第 6 階段
- 17.8.?小結
- 第?18?章?性能優化
- 18.1.?概覽
- 18.2.?使用 timeit 模塊
- 18.3.?優化正則表達式
- 18.4.?優化字典查找
- 18.5.?優化列表操作
- 18.6.?優化字符串操作
- 18.7.?小結
- 附錄?A.?進一步閱讀
- 附錄?B.?五分鐘回顧
- 附錄?C.?技巧和竅門
- 附錄?D.?示例清單
- 附錄?E.?修訂歷史
- 附錄?F.?關于本書
- 附錄 G. GNU Free Documentation License
- G.0. Preamble
- G.1.?Applicability and definitions
- G.2.?Verbatim copying
- G.3.?Copying in quantity
- G.4.?Modifications
- G.5.?Combining documents
- G.6.?Collections of documents
- G.7.?Aggregation with independent works
- G.8.?Translation
- G.9.?Termination
- G.10.?Future revisions of this license
- G.11.?How to use this License for your documents
- 附錄 H. GNU 自由文檔協議
- H.0. 序
- H.1.?適用范圍和定義
- H.2.?原樣復制
- H.3.?大量復制
- H.4.?修改
- H.5.?合并文檔
- H.6.?文檔合集
- H.7.?獨立著作聚集
- H.8.?翻譯
- H.9.?終止協議
- H.10.?協議將來的修訂
- H.11.?如何為你的文檔使用本協議
- 附錄 I. Python license
- I.A. History of the software
- I.B.?Terms and conditions for accessing or otherwise using Python
- 附錄 J. Python 協議
- J.0. 關于譯文的聲明
- J.A.?軟件的歷史
- J.B.?使用 Python 的條款和條件