天天看點

《Python高手之路(第3版)》——2.5 Doug Hellmann訪談

本節書摘來自異步社群《python高手之路(第3版)》一書中的第2章,第2.5節,作者[法]julien danjou,王飛龍 譯,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

《Python高手之路(第3版)》——2.5 Doug Hellmann訪談

**

當你從頭開發一個python應用時,如何邁出第一步呢?它和開發一個已有的應用程式有什麼不同?

從抽象角度看步驟都差不多,但是細節上有所不同。相對于對比開發新項目和已有項目,我個人在對應用程式和庫開發的處理方式上有更多的不同。

當我要修改已有代碼時,特别是這些代碼是其他人建立的時,起初我需要研究代碼是如何工作的,我需要改進哪些代碼。我可能會添加日志或是輸出語句,或是用pdb,利用測試資料運作應用程式,以便我了解它是如何工作的。我經常會做一些修改并測試它們,并在每次送出代碼前添加可能的自動化測試。

建立一個新應用時,我會采取相同的逐漸探索方法。我先建立一些代碼,然後手動運作它們,在這個功能可以基本調通後,再編寫測試用例確定我已經覆寫了所有的邊界情況。建立測試用例也可以讓代碼重構更容易。

當設計一個應用程式時,我會考慮使用者界面是如何工作的,但對于庫,我會專注于開發人員如何使用其api。通過先寫測試代碼而不是庫代碼,可以讓思考如何通過這個新庫使開發應用程式變得更容易一點兒。我通常會以測試的方式建立一系列示例程式,然後依照其工作方式去建構這個庫。

我還發現,在寫任何庫的代碼之前先寫文檔讓我可以全面考慮功能和流程的使用,而不需要送出任何實作的細節。它還讓我可以記錄我對設計所做出的選擇,以便讀者不僅可以了解如何使用這個庫,還可以了解在建立它時我的期望是什麼。這就是我用在stevedore上的方法。

我知道我想讓stevedore能夠提供一組類用來管理應用程式的插件。在設計階段,我花了些時間思考我見過的使用插件的通用模式,并且寫了幾頁粗略的文檔描述這些類應該如何使用。我意識到,如果把大部分最複雜的參數放入類的構造函數中,方法map()幾乎是可互換的。這些設計筆記直接寫進了stevedore官方文檔的簡介裡,用來解釋在應用程式中使用插件的不同模式和準則。

将一個子產品加入python标準庫的流程是什麼?

一個子產品在被加入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庫的人們有什麼樣的建議呢?

sqlalchemy是應用這些原則的絕好例子。聲明式orm、資料映射和表達式生成層都是單獨的。開發人員可以自行決定對于api通路的正确的抽象程度,并基于他們的需求而不是被庫的設計強加的限制去使用這個庫。

當你随機看python程式員的代碼時遇到的最常見的程式設計錯誤是什麼?

python的習慣用法和其他語言的一個較大的不同在于循環和疊代。例如,我見過的最常見的反模式是使用for循環過濾一個清單并将元素加入到一個新的清單中,然後再在第二個循環中處理這個結果(可能将清單作為參數傳給一個函數)。我通常建議将過濾循環改成生成器表達式,因為生成器表達式更有效也更容易了解。清單的組合也很常見,以便它們的内容可以以某種方式一起被處理,但卻沒有使用itertools.chain()。

還有一些我在代碼評審時給出的更細小的建議。例如,使用dict()而不是長的if:then:else塊作為查找表,確定函數總是傳回相同的類型(如一個空清單而不是none),通過使用元組和新類将相關的值合并到一個對象中進而減少函數的參數,以及在公共api中定義要使用的類而不是依賴于字典。

有沒有關于選擇了一個“錯誤”的依賴的具體的例子是你親身經曆或目睹過的?

你怎麼看待架構?

架構像任何工具類型一樣。它們确實有幫助,但在選擇架構時要特别謹慎,應確定它能夠很好地完成目前的工作。

通過抽取公共部分到一個架構中,你可以将你的開發精力專注于應用中獨特的方面。通過提供許多類似運作在開發模式或者寫一個測試套件這樣的引導代碼,它們還可以幫你讓一個應用程式迅速達到一個可用的狀态而不是從頭開發。它們還可以激勵你在應用程式開發過程中保持一緻,這意味着最終你的代碼将更易于了解且更可重用。

雖然使用架構時還有其他一些潛在的缺點需要注意。決定使用某個特定架構通常能夠反映應用程式本身的設計。如果設計的限制不能從根本上符合應用程式的需求,那麼選擇錯誤的架構會令應用的實作變得更難。如果你試着使用與架構建議不同的模式或慣用方式,你最終将不得不同架構做鬥争。