# 8.9.?全部放在一起
到了將迄今為止我們已經學過并用得不錯的東西放在一起的時候了。我希望您專心些。
## 例?8.20.?`translate` 函數,第 1 部分
```
def translate(url, dialectName="chef"):
import urllib
sock = urllib.urlopen(url)
htmlSource = sock.read()
sock.close()
```
| | |
| --- | --- |
| \[1\] | 這個 `translate` 函數有一個[可選參數](../power_of_introspection/optional_arguments.html "4.2.?使用可選參數和命名參數") `dialectName`,它是一個字符串,指出我們將使用的方言。一會我們就會看到它是如何使用的。 |
| \[2\] | 嘿,等一下,在這個函數中有一個 [`import`](../getting_to_know_python/everything_is_an_object.html#odbchelper.import "例?2.3.?訪問 buildConnectionString 函數的 doc string") 語句!它在 Python 中完全合法。您已經習慣了在一個程序的前面看到 `import` 語句,它意味著導入的模塊在程序的任何地方都是可用的。但您也可以在一個函數中導入模塊,這意味著導入的模塊只能在函數中使用。如果您有一個只能用在一個函數中的模塊,這是一個簡便的方法,使您的代碼更模塊化。(當發現您周末的加班已經變成了一個 800 行的藝術作品,并且決定將其分割成一打可重用的模塊時,您會感謝它的。) |
| \[3\] | 現在我們[得到了給定的 URL 源文件](extracting_data.html#dialect.extract.urllib "例?8.5.?urllib 介紹")。 |
## 例?8.21.?`translate` 函數,第 2 部分:奇妙而又奇妙
```
parserName = "%sDialectizer" % dialectName.capitalize()
parserClass = globals()[parserName]
parser = parserClass()
```
| | |
| --- | --- |
| \[1\] | `capitalize` 是一個我們以前未曾見過的字符串方法;它只是將一個字符串的第一個字母變成大寫,將其它的字母強制變成小寫。再使用[字符串格式化](../native_data_types/formatting_strings.html "3.5.?格式化字符串"),我們就得到了一種方言的名字,并將它轉化為了相應的方言變換器類的名字。如果 `dialectName` 是字符串 `'chef'`,`parserName` 將是字符串 `'ChefDialectizer'`。 |
| \[2\] | 我們有了一個字符串形式 (`parserName`) 的類名稱,還有一個 dictionary (`globals`()) 形式的全局名字空間。合起來后,我們可以得到以前者命名的類的引用。(回想一下,[類是對象](../object_oriented_framework/class_attributes.html "5.8.?類屬性介紹"),并且它們可以像其它對象一樣賦值給一個變量。) 如果 `parserName` 是字符串 `'ChefDialectizer'`,`parserClass` 將是類 `ChefDialectizer`。 |
| \[3\] | 最后,我們擁有了一個類對象 (`parserClass`),接著我們想要生成這個類的一個實例。好,我們已經知道如何去做了:[像函數一樣調用類](../object_oriented_framework/instantiating_classes.html "5.4.?類的實例化")。這個類保存在一個局部變量中,但這個事實完全不會有什么影響;我們只是像函數一樣調用這個局部變量,取出這個類的一個實例。如果 `parserClass` 是類 `ChefDialectizer`,`parser` 將是類 `ChefDialectizer` 的一個實例。 |
何必這么麻煩?畢竟只有三個 `Dialectizer` 類;為什么不只使用一個 `case` 語句? (噢,在 Python 中不存在 `case` 語句,但為什么不只使用一組 `if` 語句呢?) 理由之一是:可擴展性。這個 `translate` 函數完全不用關心我們定義了多少個方言變換器類。設想一下,如果我們明天定義了一個新的 `FooDialectizer` 類,把 `'foo'` 作為 `dialectName` 傳給 `translate` ,`translate` 也能工作。
甚至會更好。設想將 `FooDialectizer` 放進一個獨立的模塊中,使用 `from _module_ import` 將其導入。我們已經知道了,這樣會將它[包含在 `globals`()](locals_and_globals.html#dialect.globals.example "例?8.11.?globals 介紹") 中 ,所以不用修改 `translate` ,它仍然可以正確運行,盡管 `FooDialectizer` 位于一個獨立的文件中。
現在設想一下方言的名字是從程序外面的某個地方來的,也許是從一個數據庫中,或從一個表格中的用戶輸入的值中。您可以使用任意多的服務端 Python 腳本架構來動態地生成網頁;這個函數將接收在頁面請求的查詢字符串中的一個 URL 和一個方言名字 (兩個都是字符串) ,接著輸出 “翻譯” 后的網頁。
最后,設想一下,使用了一種插件架構的 `Dialectizer` 框架。您可以將每個 `Dialectizer` 類放在分別放在獨立的文件中,在 `dialect.py` 中只留下 `translate` 函數。假定一種統一的命名模式,這個 `translate` 函數能夠動態地從合適的文件中導入合適的類,除了方言名字外什么都不用給出。(雖然您還沒有看過動態導入,但我保證在后面的一章中會涉及到它。) 如果要加入一種新的方言,您只要在插件目錄下加入一個以合適的名字命名的文件 (像 `foodialect.py`,它包含了 `FooDialectizer` 類) 。使用方言名 `'foo'` 來調用這個 `translate` 函數,將會查找 `foodialect.py` 模塊,導入 `FooDialectizer` 類,這樣就行了。
## 例?8.22.?`translate` 函數,第 3 部分
```
parser.feed(htmlSource)
parser.close()
return parser.output()
```
| | |
| --- | --- |
| \[1\] | 剩下的工作似乎會非常無聊,但實際上,`feed` 函數[執行了全部的轉換工作](extracting_data.html#dialect.feed.example "例?8.7.?使用 urllister.py")。我們擁有存在于單個字符串中的全部 HTML 源代碼,所以我們只需要調用 `feed` 一次。然而,您可以按您的需要經常調用 `feed`,分析器將不停地進行分析。所以如果我們擔心內存的使用 (或者我們已經知道了將要處理非常巨大的 HTML 頁面) ,我們可以在一個循環中調用它,即我們讀出一點 HTML 字節,就將其送進分析器。結果會是一樣的。 |
| \[2\] | 因為 `feed` 維護著一個內部緩沖區,當您完成時,應該總是調用分析器的 `close` 方法 (那怕您像我們做的一樣,一次就全部送出) 。否則您可能會發現,輸出丟掉了最后幾個字節。 |
| \[3\] | 回想一下,`output` 是我們在 `BaseHTMLProcessor` 上定義的函數,用來[將所有緩沖的輸出片段連接起來](basehtmlprocessor.html#dialect.output.example "例?8.9.?BaseHTMLProcessor 輸出結果")并且以單個字符串返回。 |
像這樣,我們已經 “翻譯” 了一個網頁,除了給出一個 URL 和一種方言的名字外,什么都沒有給出。
## 進一步閱讀
* 您可能會認為我的服務端腳本編程的想法是開玩笑。在我發現這個[基于 web 的方言轉換器](http://rinkworks.com/dialect/)之前,的確是這樣想的。不幸的是,看不到它的源代碼。
- 版權信息
- 第?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 的條款和條件