我曾經有幸和Doug Hellmann一起工作過數月。他在DreamHost是一位非常資深的軟件開發工程師,同時他也是OpenStack項目的貢獻者。他發起過關于Python的網站Python Module of the Week(<http://pymotw.com/>),也出版過一本很有名的Pyhton書*The Python Standard Library By Example*(<http://doughellmann.com/python-standard-library-by-example>),同時他也是Python的核心開發人員。我曾經咨詢過Doug關于標準庫以及庫的設計與應用等方面的問題。

**當你從頭開發一個Python應用時,如何邁出第一步呢?它和開發一個已有的應用程序有什么不同?**
從抽象角度看步驟都差不多,但是細節上有所不同。相對于對比開發新項目和已有項目,我個人在對應用程序和庫開發的處理方式上有更多的不同。
當我要修改已有代碼時,特別是這些代碼是其他人創建的時,起初我需要研究代碼是如何工作的,我需要改進哪些代碼。我可能會添加日志或是輸出語句,或是用pdb,利用測試數據運行應用程序,以便我理解它是如何工作的。我經常會做一些修改并測試它們,并在每次提交代碼前添加可能的自動化測試。
創建一個新應用時,我會采取相同的逐步探索方法。我先創建一些代碼,然后手動運行它們,在這個功能可以基本調通后,再編寫測試用例確保我已經覆蓋了所有的邊界情況。創建測試用例也可以讓代碼重構更容易。
這正是smiley(<https://pypi.python.org/pypi/smiley>)的情況。在開發正式應用程序前,我先嘗試用Python的trace API寫一些臨時腳本。對于smiley我最初的設想包括一個儀表盤并從另一個運行的應用程序收集數據,另一部分用來接收通過網絡發送過來的數據并將其保存。在添加幾個不同的報告功能的過程中,我意識到重放已收集的數據的過程和在一開始收集數據的過程基本是一樣的。于是我重構了一些類,并針對數據收集、數據庫訪問和報告生成器創建了基類。通過讓這些類遵循同樣的API使我可以很容易地創建數據收集應用的一個版本,它可以直接將數據寫入數據庫而無需通過網絡發送數據。
當設計一個應用程序時,我會考慮用戶界面是如何工作的,但對于庫,我會專注于開發人員如何使用其API。通過先寫測試代碼而不是庫代碼,可以讓思考如何通過這個新庫使開發應用程序變得更容易一點兒。我通常會以測試的方式創建一系列示例程序,然后依照其工作方式去構建這個庫。
我還發現,在寫任何庫的代碼之前先寫文檔讓我可以全面考慮功能和流程的使用,而不需要提交任何實現的細節。它還讓我可以記錄我對設計所做出的選擇,以便讀者不僅可以理解如何使用這個庫,還可以了解在創建它時我的期望是什么。這就是我用在stevedore上的方法。
我知道我想讓stevedore能夠提供一組類用來管理應用程序的插件。在設計階段,我花了些時間思考我見過的使用插件的通用模式,并且寫了幾頁粗略的文檔描述這些類應該如何使用。我意識到,如果把大部分最復雜的參數放入類的構造函數中,方法`map()`幾乎是可互換的。這些設計筆記直接寫進了stevedore官方文檔的簡介里,用來解釋在應用程序中使用插件的不同模式和準則。
**將一個模塊加入Python標準庫的流程是什么?**
完整的流程和規范可以在Python Developer’s Guide([http://docs.python.org/devguide/ stdlibchanges.html](http://docs.python.org/devguide/stdlibchanges.html))中找到。
一個模塊在被加入Python標準庫之前,需要被證明是穩定且廣泛使用的。模塊需要提供的功能要么是很難正確實現的,要么是非常有用以至于許多開發人員已經創建了他們自己不同的變種。API應該非常清晰并且它的實現不能依賴任何標準庫之外的庫。
提議一個新模塊的第一步是在社區通過python-ideas郵件列表非正式地了解一下大家對此的感興趣程度。如果回應很積極,下一步就是創建一個Python增強提案(Python Enhancement Proposal,PEP),它包括添加這個模塊的動機,以及如何過渡的一些實現細節。
因為包的管理和發現工作已經非常穩定了,尤其是pip和Python Package Index(PyPI),因此在標準庫之外維護一個新的庫可能更實用。單獨的發布使得對于新功能和bug修復(bugfix)的更新可以更頻繁,對于處理新技術或API的庫來說這尤其重要。
**標準庫中的哪3個模塊是你最想讓人們深入了解并開始使用的?**
最近我做了許多關于應用程序中動態加載擴展方面的工作。我使用****abc****模塊為那些作為抽象基類進行的擴展定義API,以幫助擴展的作者們了解API的哪些方法是必需的,哪些是可選的。抽象基類已經在其他一些語言中內置了,但我發現很多Python程序員并不知道Python也有。
**bisect**模塊中的二分查找算法是個很好的例子,一個廣泛使用但不容易正確實現的功能,因此它非常適合放到標準庫中。我特別喜歡它可以搜索稀疏列表,且搜索的值可能并不在其中。
**collections**模塊中有許多有用的數據結構并沒有得到廣泛使用。我喜歡用**namedtuple**來創建一些小的像類一樣的數據結構來保存數據但并不需要任何關聯邏輯。如果之后需要添加邏輯的話,可以很容易將**namedtuple**轉換成一個普通的類,因為namedtuple支持通過名字訪問屬性。另一個有意思的數據結構是**ChainMap**,它可以生成良好的層級命名空間。ChainMap能夠用來為模板解析創建上下文或者通過清晰的流程定義來管理不同來源的配置。
**許多項目(包括OpenStack)或者外部庫,會在標準庫之上封裝一層自己的抽象。例如,我特別想了解對于日期/時間的處理。對此你有什么建議嗎?程序員應該堅持使用標準庫,還是應該寫他們自己的函數,切換到其他外部庫或是開始給Python提交補丁?**
所有這些都可以。我傾向于避免重復造輪子,所以我強烈主張貢獻補丁和改進那些能夠用來作為依賴的項目。但是,有時創建另外的抽象并單獨維護代碼也是合理的,不管在應用程序內還是作為一個新的庫。
你提到的例子中,OpenStack里的**timeutils**模塊就是對Python的**datetime**模塊的一層很薄的封裝。大部分功能都簡短且簡單,但通過將這些最常見的操作封裝為一個模塊,我們可以保證它們在OpenStack項目中以一致的方式進行處理。因為許多函數都是應用相關的,某種意義上它們強化了一些問題決策,例如,字符串時間戳格式或者“現在”意味著什么,它們不太適合作為Python標準庫的補丁或者作為一個通用庫發布以及被其他項目采用。
與之相反,我目前正致力于將OpenStack的API服務項目從早期創建時使用的WSGI框架轉成采用一個第三方Web開發框架。在Python中開發WSGI應用有很多選擇,并且當我們可能需要增強其中一個以便其可以完全適應OpenStack API服務器的需要時,將這些可重用的修改貢獻對于維護一個“私有的”框架似乎更可取。
**當從標準庫或其他地方導入并使用大量模塊時,關于該做什么你有什么特別的建議嗎?**
我沒有什么硬性限制,但是如果我有過多的導入時,我會重新考慮這個模塊的設計并考慮將其拆到一個包中。與上層模塊或者應用程序模塊相比,對底層模塊的這種拆分可能會發生得更快,因為對于上層模塊我期望將更多片段組織在一起。
**關于Python 3,有什么模塊是值得一提而且能令開發人員有興趣深入了解的?**
支持Python 3的第三方庫的數量已經到了決定性的時刻。針對Python 3開發新庫或應用程序從未如此簡單過,而且幸虧有3.3中加入的兼容性功能使同時維護對Python 2.7的支持也很容易。主要的Linux發行版正在致力于將Python 3默認安裝。任何人要用Python創建新項目都應該認真考慮對Python 3的支持,除非有尚未移植的依賴。目前來說,不能運行在Python 3上的庫基本會被視為“不再維護”。
**許多開發人員將所有的代碼都寫入到應用程序中,但有些情況下可能有必要將代碼封裝成一個庫。關于設計、規劃、遷移等,做這些最好的方式是什么?**
應用程序就是“膠水代碼”的集合用來將庫組織在一起完成特定目的。起初設計時可以將這些功能實現為一個庫,然后在構建應用程序時確保庫的代碼能夠很好地組織到邏輯單元中,這會讓測試變得更簡單。這還意味著應用程序的功能可以通過庫進行訪問,并且能夠被重新組合以構建其他應用程序。未能采用這種方法的話意味著應用程序的功能和用戶界面的綁定過于緊密,導致很難修改和重用。
**對于計劃開始構建自己的Python庫的人們有什么樣的建議呢?**
我通常建議自頂向下設計庫和API,對每一層應用單一職責原則(Single Responsibility Principle,SRP)([http://en.wikipedia.org/wiki/Single\_responsibility\_principle](http://en.wikipedia.org/wiki/Single_responsibility_principle))這樣的設計準則。考慮調用者如何使用這個庫,并創建一個API去支持這些功能。考慮什么值可以存在一個實例中被方法使用,以及每個方法每次都要傳入哪些值。最后,考慮實現以及是否底層的代碼的組織應該不同于公共API。
****SQLAlchemy****是應用這些原則的絕好例子。聲明式ORM、數據映射和表達式生成層都是單獨的。開發人員可以自行決定對于API訪問的正確的抽象程度,并基于他們的需求而不是被庫的設計強加的約束去使用這個庫。
**當你隨機看Python程序員的代碼時遇到的最常見的編程錯誤是什么?**
Python的習慣用法和其他語言的一個較大的不同在于循環和迭代。例如,我見過的最常見的反模式是使用`for`循環過濾一個列表并將元素加入到一個新的列表中,然后再在第二個循環中處理這個結果(可能將列表作為參數傳給一個函數)。我通常建議將過濾循環改成生成器表達式,因為生成器表達式更有效也更容易理解。列表的組合也很常見,以便它們的內容可以以某種方式一起被處理,但卻沒有使用`itertools.chain()`。
還有一些我在代碼評審時給出的更細小的建議。例如,使用`dict()`而不是長的`if:then:else`塊作為查找表,確保函數總是返回相同的類型(如一個空列表而不是None),通過使用元組和新類將相關的值合并到一個對象中從而減少函數的參數,以及在公共API中定義要使用的類而不是依賴于字典。
**有沒有關于選擇了一個“錯誤”的依賴的具體的例子是你親身經歷或目睹過的?**
最近,我有個例子,pyparsing(<https://pypi.python.org/pypi/pyparsing>)的一個新發布取消了對Python 2的支持,這給我正在維護的一個庫帶來了一點兒小麻煩。對pyparsing的更新是個重大的修改,而且是明確標識成這樣的,但是因為我沒有在對cliff(<https://pypi.python.org/pypi/cliff>)的設置中限制依賴版本號,所以pyparsing的新發布給cliff的用戶造成了問題。解決方案就是在cliff的依賴列表中對Python 2和Python 3提供不同的版本邊界。這種情況突顯了理解依賴管理和確保持續集成測試中適當的測試配置的重要性。
**你怎么看待框架?**
框架像任何工具類型一樣。它們確實有幫助,但在選擇框架時要特別謹慎,應確保它能夠很好地完成當前的工作。
通過抽取公共部分到一個框架中,你可以將你的開發精力專注于應用中獨特的方面。通過提供許多類似運行在開發模式或者寫一個測試套件這樣的引導代碼,它們還可以幫你讓一個應用程序迅速達到一個可用的狀態而不是從頭開發。它們還可以激勵你在應用程序開發過程中保持一致,這意味著最終你的代碼將更易于理解且更可重用。
雖然使用框架時還有其他一些潛在的缺點需要注意。決定使用某個特定框架通常能夠反映應用程序本身的設計。如果設計的限制不能從根本上符合應用程序的需求,那么選擇錯誤的框架會令應用的實現變得更難。如果你試著使用與框架建議不同的模式或慣用方式,你最終將不得不同框架做斗爭。
- 內容提要
- 中文版序
- 前言
- 第1章 項目開始
- 1.1 Python版本
- 1.2 項目布局
- 1.3 版本編號
- 1.4 編碼風格與自動檢查
- 1.5 Joshua Harlow訪談
- 第2章 模塊和庫
- 2.1 導入系統
- 2.2 標準庫
- 2.3 外部庫
- 2.4 框架
- 2.5 Doug Hellmann訪談
- 第3章 管理API變化
- Christophe de Vienne訪談
- 第4章 時區陷阱
- 第5章 文檔
- 5.1 Sphinx和reST入門
- 5.2 Sphinx模塊
- 5.3 擴展Sphinx
- 第6章 分發
- 6.1 簡史
- 6.2 使用pbr打包
- 6.3 Wheel格式
- 6.4 包的安裝
- 6.5 和世界分享你的成果
- 6.6 Nick Coghlan訪談
- 6.7 入口點
- 6.7.1 可視化的入口點
- 6.7.2 使用控制臺腳本
- 6.7.3 使用插件和驅動程序
- 第7章 虛擬環境
- 第8章 單元測試
- 8.1 基礎知識
- 8.2 fixture
- 8.3 模擬(mocking)
- 8.4 場景測試
- 8.5 測試序列與并行
- 8.6 測試覆蓋
- 8.7 使用虛擬環境和tox
- 8.8 測試策略
- 8.9 Robert Collins訪談
- 第9章 方法和裝飾器
- 9.1 創建裝飾器
- 9.2 Python中方法的運行機制
- 9.3 靜態方法
- 9.4 類方法
- 9.5 抽象方法
- 9.6 混合使用靜態方法、類方法和抽象方法
- 9.7 關于super的真相
- 第10章 函數式編程
- 10.1 生成器
- 10.2 列表推導
- 10.3 函數式函數的函數化
- 第11章 抽象語法樹
- 11.1 用抽象語法樹檢查來擴展flake8
- 11.2 Hy
- 11.3 Paul Tagliamonte訪談
- 第12章 性能與優化
- 12.1 數據結構
- 12.2 性能分析
- 12.3 有序列表和二分查找
- 12.4 namedtuple和slots
- 12.5 memoization
- 12.6 PyPy
- 12.7 通過緩沖區協議實現零復制
- 12.8 Victor Stinner訪談
- 第13章 擴展與架構
- 13.1 多線程的注意事項
- 13.2 多進程與多線程
- 13.3 異步和事件驅動架構
- 13.4 面向服務架構
- 第14章 RDBMS和ORM
- 14.1 用Flask和PostgreSQL流化數據
- 14.2 Dimitri Fontaine訪談
- 第15章 Python 3支持策略
- 15.1 語言和標準庫
- 15.2 外部庫
- 15.3 使用six
- 第16章 少即是多
- 16.1 單分發器
- 16.2 上下文管理器
- 第17章 延伸閱讀
- 版權信息
- 版權聲明
- 歡迎來到異步社區!
- 異步社區的來歷
- 社區里都有什么?
- 購買圖書
- 下載資源
- 與作譯者互動
- 靈活優惠的購書
- 紙電圖書組合購買
- 社區里還可以做什么?
- 提交勘誤
- 寫作
- 會議活動早知道
- 加入異步