【前 言】
Python是一門強大的編程語言,它很有魅力,同時也很獨特,所以掌握起來或許有點兒困難。許多程序員從他們熟悉的語言轉到Python之后都打不開思路,所以寫出來的代碼沒有充分發(fā)揮出Python的特性。還有一些程序員則相反,他們會濫用Python的特性,這樣寫出來的程序以后可能會出現(xiàn)嚴重的問題。
本書會詳細地告訴大家如何采用符合Python風格的方式(Pythonic方式)來編寫程序,這是使用Python語言的最佳方式。筆者假設你對這門語言已經(jīng)有了初步了解。編程新手可以通過這本書學到各種Python功能的最佳用法,有經(jīng)驗的程序員則能夠學會如何自信地運用Python中的新工具。
筆者的目標是讓大家用Python開發(fā)出優(yōu)秀的軟件。
【本書涵蓋的內容】
本書每一章都包含許多相互關聯(lián)的條目。大家可以按照自己的需要隨意閱讀這些條目。每個條目都包含簡潔而明確的教程,告訴你如何才能更有效率地編寫Python程序。筆者在每個條目里都給出了建議,告訴大家哪些應該做,哪些應該避免,以及怎樣在各種做法之間求得平衡,并且會解釋筆者所選的做法好在哪里。筆者可能會在某個條目中提到其他一些條目,讓大家可以全面地了解這些建議所涉及的知識。
本書第2版只針對Python 3(參見第1條),這里說的Python 3包含從3.0到3.8的各個版本。本書第1版中的許多條目仍然收錄在第2版中,并且做了修訂,其中有些條目改動比較大。隨著Python語言越來越成熟,最佳編程方法也在演變,所以筆者在第2版中對某些問題的建議,可能與第1版的完全不同。Python 2已經(jīng)在2020年1月1日退場,如果你還是主要在用這個版本來編程,那么第1版中的建議或許比第2版中的建議更加合適。
Python采用“自帶電池”(batteries included)的理念來設計標準庫,不像其他語言那樣只提供少數(shù)幾個常用的軟件包,如果需要的重要功能不在這些軟件包里,那就要自己去尋找了。許多Python內置軟件包與Python的習慣用法有著密切的關系,所以實際上已經(jīng)成了語言規(guī)范的一部分。本書篇幅有限,不可能把所有的標準模塊全都講一遍,但會涵蓋其中那些需要了解和使用且用法比較關鍵的模塊。
●第1章:培養(yǎng) Pythonic 思維
Python開發(fā)者社區(qū)用Pythonic這個形容詞來描述具有某種特定風格的代碼。這種風格是大家在使用Python語言編程并相互協(xié)作的過程中逐漸形成的。本章講解如何采用這樣的風格編寫常見的Python代碼。
●第2章:列表與字典
在Python語言中整理信息時,最常用的方法是把一系列數(shù)值保存到列表(list)中。既然有列表,那就有另外一種跟它互補的結構,也就是字典(dict),這種結構可以把它存儲的查找鍵映射到對應的值上。本章講解如何采用這些數(shù)據(jù)結構來編寫程序。
●第3章:函數(shù)
Python中的函數(shù)具備多種特性,這有助于簡化編程工作。Python函數(shù)的某些性質與其他編程語言中函數(shù)的類似,但也有一些是Python獨有的。本章介紹如何使用函數(shù)來表達開發(fā)者的意圖,如何讓代碼更容易復用,以及如何減少bug。
●第4章:推導與生成
Python有一種特殊的語法,可以迅速迭代列表(list)、字典(dict)與集合(set),并據(jù)此生成相應的數(shù)據(jù)結構,這讓我們能夠在函數(shù)返回的這種結構上逐個訪問根據(jù)原結構所派生出來的一系列值。本章講解怎樣利用這種機制來提升程序效率并降低內存用量,同時讓代碼變得更容易讀懂。
●第5章:類與接口
Python是面向對象的語言。用Python編程時,經(jīng)常要編寫新的類,而且還要定義這些類應該如何通過其接口以及繼承體系與其他代碼相交互。本章講解怎樣使用類來表達對象所應具備的行為。
●第 6 章:元類與屬性
元類(metaclass)與動態(tài)屬性(dynamic attribute)都是很強大的Python特性,但它們也有可能會讓程序出現(xiàn)古怪的行為與意外的效果。本章講解這些機制的習慣用法,確保讀者寫出來的代碼遵循最小驚訝原則(rule of least surprise)。
●第7章:并發(fā)與并行
用Python很容易寫并發(fā)程序,這種程序可以在同一時刻做許多件不同的事情。Python也可以通過系統(tǒng)調用、子進程以及C語言擴展來實現(xiàn)并行處理。本章介紹這些Python特性應該用在什么情況下。
●第8章:穩(wěn)定與性能
Python內置了一些功能與模塊,可以讓程序變得更加可靠。另外,Python還提供了一些工具,可以讓我們輕松地提升程序的性能。本章講解怎樣用Python優(yōu)化程序,讓這些程序在正式的運行環(huán)境中表現(xiàn)得更加穩(wěn)定、更加高效。
●第 9 章:測試與調試
不管使用哪種語言編程,我們都應該把寫出來的代碼測試一下。但對于Python來說,還有個特殊的問題,那就是它所提供的動態(tài)機制可能會增加程序在運行時出現(xiàn)錯誤的風險。好在Python也讓我們可以比較容易地編寫測試代碼和故障診斷程序。本章講解怎樣用Python內置的工具來測試并調試程序。
●第 10 章:協(xié)作開發(fā)
如果許多人要協(xié)作開發(fā)一個Python程序,那就得仔細商量代碼的寫法了。即便你只是一個人開發(fā),也需要了解如何使用其他人所寫的模塊。本章介紹標準的工具以及業(yè)界總結出來的最佳方法,告訴大家怎樣協(xié)作開發(fā)Python程序。
【獲取源代碼及勘誤表】
微信關注“華章計算機”,回復“67402”,可免費獲取本書源代碼下載地址,并加入讀者交流群。拿到范例代碼之后,可以拋開書中講解代碼的那些內容,單獨把這個完整的程序運行一遍,你可以用這些代碼做實驗,并試著理解程序為什么會這樣運行。
●第1章 培養(yǎng)Pythonic思維 // 1
第1條 查詢自己使用的Python版本 // 1
第2條 遵循PEP 8風格指南 // 2
第3條 了解bytes與str的區(qū)別 // 5
第4條 用支持插值的f-string取代C風格的格式字符串與str.format方法 // 9
第5條 用輔助函數(shù)取代復雜的表達式 // 19
第6條 把數(shù)據(jù)結構直接拆分到多個變量里,不要專門通過下標訪問 // 21
第7條 盡量用enumerate取代range // 25
第8條 用zip函數(shù)同時遍歷兩個迭代器 // 26
第9條 不要在for與while循環(huán)后面寫else塊 // 28
第10條 用賦值表達式減少重復代碼 // 31
●第2章 列表與字典 // 37
第11條 學會對序列做切片 // 37
第12條 不要在切片里同時指定起止下標與步進 // 40
第13條 通過帶星號的unpacking操作來捕獲多個元素,不要用切片 // 42
第14條 用sort方法的key參數(shù)來表示復雜的排序邏輯 // 45
第15條 不要過分依賴給字典添加條目時所用的順序 // 51
第16條 用get處理鍵不在字典中的情況,不要使用in與KeyError // 56
第17條 用defaultdict處理內部狀態(tài)中缺失的元素,而不要用setdefault // 61
第18條 學會利用__missing__構造依賴鍵的默認值 // 63
●第3章 函數(shù) // 66
第19條 不要把函數(shù)返回的多個數(shù)值拆分到三個以上的變量中 // 66
第20條 遇到意外狀況時應該拋出異常,不要返回None // 69
第21條 了解如何在閉包里面使用外圍作用域中的變量 // 71
第22條 用數(shù)量可變的位置參數(shù)給函數(shù)設計清晰的參數(shù)列表 // 75
第23條 用關鍵字參數(shù)來表示可選的行為 // 77
第24條 用None和docstring來描述默認值會變的參數(shù) // 80
第25條 用只能以關鍵字指定和只能按位置傳入的參數(shù)來設計清晰的參數(shù)列表 // 83
第26條 用functools.wraps定義函數(shù)修飾器 // 88
●第4章 推導與生成 // 91
第27條 用列表推導取代map與filter // 91
第28條 控制推導邏輯的子表達式不要超過兩個 // 93
第29條 用賦值表達式消除推導中的重復代碼 // 94
第30條 不要讓函數(shù)直接返回列表,應該讓它逐個生成列表里的值 // 97
第31條 謹慎地迭代函數(shù)所收到的參數(shù) // 100
第32條 考慮用生成器表達式改寫數(shù)據(jù)量較大的列表推導 // 104
第33條 通過yield from把多個生成器連起來用 // 106
第34條 不要用send給生成器注入數(shù)據(jù) // 108
第35條 不要通過throw變換生成器的狀態(tài) // 113
第36條 考慮用itertools拼裝迭代器與生成器 // 117
●第5章 類與接口 // 122
第37條 用組合起來的類來實現(xiàn)多層結構,不要用嵌套的內置類型 // 122
第38條 讓簡單的接口接受函數(shù),而不是類的實例 // 128
第39條 通過@classmethod多態(tài)來構造同一體系中的各類對象 // 131
第40條 通過super初始化超類 // 136
第41條 考慮用mix-in類來表示可組合的功能 // 140
第42條 優(yōu)先考慮用public屬性表示應受保護的數(shù)據(jù),不要用private屬性表示 // 144
第43條 自定義的容器類型應該從collections.abc繼承 // 149
●第6章 元類與屬性 // 153
第44條 用純屬性與修飾器取代舊式的setter與getter方法 // 153
第45條 考慮用@property實現(xiàn)新的屬性訪問邏輯,不要急著重構原有的代碼 // 157
第46條 用描述符來改寫需要復用的@property方法 // 160
第47條 針對惰性屬性使用__getattr__、__getattribute__及__setattr__ // 165
第48條 用__init_subclass__驗證子類寫得是否正確 // 170
第49條 用__init_subclass__記錄現(xiàn)有的子類 // 177
第50條 用__set_name__給類屬性加注解 // 181
第51條 優(yōu)先考慮通過類修飾器來提供可組合的擴充功能,不要使用元類 // 185
●第7章 并發(fā)與并行 // 191
第52條 用subprocess管理子進程 // 192
第53條 可以用線程執(zhí)行阻塞式I/O,但不要用它做并行計算 // 195
第54條 利用Lock防止多個線程爭用同一份數(shù)據(jù) // 199
第55條 用Queue來協(xié)調各線程之間的工作進度 // 202
第56條 學會判斷什么場合必須做并發(fā) // 210
第57條 不要在每次fan-out時都新建一批Thread實例 // 214
第58條 學會正確地重構代碼,以便用Queue做并發(fā) // 218
第59條 如果必須用線程做并發(fā),那就考慮通過ThreadPoolExecutor實現(xiàn) // 224
第60條 用協(xié)程實現(xiàn)高并發(fā)的I/O // 226
第61條 學會用asyncio改寫那些通過線程實現(xiàn)的I/O // 230
第62條 結合線程與協(xié)程,將代碼順利遷移到asyncio // 239
第63條 讓asyncio的事件循環(huán)保持暢通,以便進一步提升程序的響應能力 // 245
第64條 考慮用concurrent.futures實現(xiàn)真正的并行計算 // 248
●第8章 穩(wěn)定與性能 // 253
第65條 合理利用try/except/else/finally結構中的每個代碼塊 // 253
第66條 考慮用contextlib和with語句來改寫可復用的try/finally代碼 // 258
第67條 用datetime模塊處理本地時間,不要用time模塊 // 262
第68條 用copyreg實現(xiàn)可靠的pickle操作 // 265
第69條 在需要準確計算的場合,用decimal表示相應的數(shù)值 // 272
第70條 先分析性能,然后再優(yōu)化 // 274
第71條 優(yōu)先考慮用deque實現(xiàn)生產(chǎn)者-消費者隊列 // 278
第72條 考慮用bisect搜索已排序的序列 // 284
第73條 學會使用heapq制作優(yōu)先級隊列 // 286
第74條 考慮用memoryview與bytearray來實現(xiàn)無須拷貝的bytes操作 // 294
●第9章 測試與調試 // 300
第75條 通過repr字符串輸出調試信息 // 301
第76條 在TestCase子類里驗證相關的行為 // 304
第77條 把測試前、后的準備與清理邏輯寫在setUp、tearDown、setUpModule與tearDownModule中,以防用例之間互相干擾 // 309
第78條 用Mock來模擬受測代碼所依賴的復雜函數(shù) // 312
第79條 把受測代碼所依賴的系統(tǒng)封裝起來,以便于模擬和測試 // 319
第80條 考慮用pdb做交互調試 // 322
第81條 用tracemalloc來掌握內存的使用與泄漏情況 // 326
●第10章 協(xié)作開發(fā) // 329
第82條 學會尋找由其他Python開發(fā)者所構建的模塊 // 329
第83條 用虛擬環(huán)境隔離項目,并重建依賴關系 // 330
第84條 每一個函數(shù)、類與模塊都要寫docstring // 335
第85條 用包來安排模塊,以提供穩(wěn)固的API // 339
第86條 考慮用模塊級別的代碼配置不同的部署環(huán)境 // 344
第87條 為自編的模塊定義根異常,讓調用者能夠專門處理與此API有關的異!// 346
第88條 用適當?shù)姆绞酱蚱蒲h(huán)依賴關系 // 350
第89條 重構時考慮通過warnings提醒開發(fā)者API已經(jīng)發(fā)生變化 // 355
第90條 考慮通過typing做靜態(tài)分析,以消除bug // 361