Python進階自檢清單:來自《Effective Python》的建議(三)

Python進階自檢清單:來自《Effective Python》的建議(三)

本系列主要介紹《Effective Python》所推薦的最佳實踐,經驗豐富的程序員應該對這些實踐都比較熟悉了。文中有些補充信息是我自己的理解,請大家多指教!具體分析還請參考原書內容。


五:併發與並行(6條建議)

1. 用subprocess模塊來管理子進程;

這裡涉及到 subprocess 和 multiprocessing 模塊的異同問題,一般 subprocess 用於在 Python 代碼中調用其它程序,特別是不是用 Python 代碼寫的程序,比如 cmd 命令,開啟其它應用等;而 multiprocessing 的接口和 threading 很相似,常用於分解我們自己代碼中的任務,加快計算速度。

2. 用多線程處理 I/O 密集型任務,但不要用來處理計算密集型任務;

3. 在多線程中使用 Lock 來防止數據競爭;

這裡涉及到對 GIL 的理解問題。由於 GIL 的存在,Python 中的多線程沒法並行執行。如果我們進行一些測試,也會發現多個線程對同一個對象進行修改,一般是不會出錯的。

但是,這個不出錯純粹是概率問題,因為 GIL 進行線程切換的過程對我們是不透明的。Python3.2 之前使用解釋器計步(ticks),經過一定計算量就切換線程;3.2 之後對線程時間進行統計,一個線程佔用 5 毫秒之後,如果有其它線程請求 GIL 鎖,則會切換線程。

一些我們看上去的原子操作,實際上是涉及多個步驟的,比如 += 等,如果在步驟之間發生了線程切換,就會導致錯誤。因此,多線程處理同一個對象還是要加互斥鎖的。

4. 用 Queue 來協調各線程之間的工作;

5. 用協程來併發地運行多個函數;

一個正在執行的線程需要大約 8MB 內存,一個協程佔用不到 1KB;而且線程啟動慢,為了實現線程安全還要寫不少輔助代碼。

python3.5 之後,引入async 和 await 關鍵字,協程的使用明確了很多。

6. 考慮用 multiprocessing 實現多線程;

multiprocessing 開銷比較大,因為主進程和子進程之間的通信,必須進行序列化和反序列化操作。如果進程之間要傳輸很多數據,而每個進程的計算量相對較小,必須通過一些高級特性,比如共享內存來避免開銷過高帶來的問題。但一般情況下,不建議使用這些高級特性。


六:內置模塊(7條建議)

1. 用 functools.wraps 定義函數修飾器;

解決命名空間規則帶來的被裝飾函數的名稱改變的問題。

2. 考慮用 contextlib 和 with 語句來改寫可複用的 try/finally 語句;

3. 用 copyreg 實現可靠的 pickle 操作;

使用很麻煩,數據結構複雜時,建議還是直接拋棄 pickle,用 json 保存數據。

4. 用 datetime 模塊來處理本地時間,而不是 time 模塊;

time 模塊依賴操作系統,沒法處理不同時區之間的轉換,datetime 處理時區實際上也依賴另一個模塊,即 pytz 對時區的定義。開發者應該以 UTC 為中介進行時區轉換。

5. 使用內置算法和數據結構;

如 deque、OrderedDict、defaultdict、heap 等常用數據結構,bisect 二分操作模塊,以及 itertools 這個迭代器常用函數包。

6. 高精度計算應該使用 decimal;

7. 學會搜索和使用第三方模塊


七:協作開發(5條建議)

1. 為每個函數、類和模塊編寫文檔;

2. 用包來安排模塊,並提供穩固的 API;

通過 __all__ 屬性和 __init__.py 文件提供公共 API ,避免內部結構的暴露,之後修改包的內容,也不會影響舊的調用代碼。

3. 為自編的模塊定義根異常,以便將調用者與 API 相隔離;

4. 學會處理循環依賴問題;

5. 使用虛擬環境;


八:部署(6條建議)

1. 考慮用單獨的文件來配置不同的部署環境,一般即 config.py;

2. 通過 repr 字符串來輸出調試信息;

3. 用 unittest 來測試全部代碼;

4. 考慮用 pdb 實現交互調試;

5. 先分析性能,然後再優化;

6. 用 tracemalloc 來掌握內存的使用及洩露情況;


END


分享到:


相關文章: