《Python進階程式設計》(第二版) ——速查筆記 第10~14章 完結
- 第十章 測試驅動開發 TDD test-driven Development
-
- 1. 簡單的測試介紹
- 2.測試的提高部分
- 3.小結
- 第十一章 優化 一般分析原則與分析技術
-
- 1. 3個優化原則
- 2. 優化政策
- 3. 查找瓶頸
- 第十二章 優化 一些強大的技術
-
- 1. 降低複雜度
- 2. 簡化
- 3. 使用集合子產品
- 4. 架構體系的權衡
- 5. 緩存
- 6. 小結
- 第十三章 并發
-
- 1. 為何需要并發
- 2. 多線程
- 3. 多程序
- 4. 異步程式設計
- 5.小結
- 第十四章 有用的設計模式
-
- 1. 建立型模式 (creational patterns)
- 2. 結構型模式(structural patterns)
- 3. 行為模式(behavioral patterns)
第十章 測試驅動開發 TDD test-driven Development
1. 簡單的測試介紹
- 為何使用TDD
- 可以在實際軟體未開始,或者函數未實作的情況下,編寫測試用例并不斷壯大測試用例,以完成測試用例為目标來開發軟體。
- 可以防止軟體回歸(新版本發生了舊版本發生的問題)因為是測試驅動開發,是以測試不可少,利于發現問題位置。
- 提高代碼品質 将開發者關注點放在業務邏輯上,提高代碼品質。
- 利用測試可以提高軟體内部原理最好的解釋
- 更快的編寫更健壯的代碼 使用TDD能讓代碼調試的時間減短,減少代碼建構時間(局部測試、降低建構時間)
- 日常存在那些測試
- 驗收測試 由客戶或者客戶目标構件的最終測試,通過這種測試後認為其具備傳遞條件。
- 單元測試 可以測試子產品 函數 代碼塊等 最細顆粒度的測試
- 功能測試 針對某種功能的測試 在給定替代環境下檢測單一功能的測試
- 內建測試,與功能測試相近比其測試的子產品級别更高,也是在給定非生産環境下的測試。
- 負載和性能測試 web負載測試,隻提供名額值不提供最終結果(需要人為判定)
- 代碼品質測試 風格違例、文檔數量 複雜性名額 靜态代碼分析警告等
- 常用測試工具
- unittest (單元\子產品測試工具)
- unittest 标準庫内元件
- 複寫unittest.TestCase 類來建立測試案例 用 unittest.main()方法來調啟測試。
類内各個測試 需要用
test_
開頭,
- 可以寫一個 test_suite方法來包裹多個測試的類,進行多個類的測試
2. doctest
-簡單了解 特殊的rst文檔中記錄需要的函數和期望輸出,以此來确定測試結果。
2.測試的提高部分
- unittest的問題
- 範闆代碼多,臃腫
- 擴充難
- 難以管理群組織複雜測試 分setup階段和teardown階段是綁定到testcase上 難以修改
- 較難收集測試結果組織測試
- 替代品 NOSE
- 使用 pip install nose 安裝
- 使用 python -m nosetest -v在目前目錄下搜尋所有可以測試的類和函數(與unittest定義差不多)
- 編寫測試固件(腳本) 可以編寫 包 子產品 測試 三個級别 三個附加函數setup固件加載前 teardown固件加載後 以及 自定義固件加載流程 用with_setup方法來加載相關方法
- 在setuptool 中 提供 test_suite="nose.collector" 值來內建nose
- 用的多還可以在測試檔案夾中使用 .noserc 或者 nose.cfg 搞一些全局性配置
- 仍需遵守unittest的函數命名約定
- 替代品 py.test
- pip install pytest
- 同樣自動搜尋相應的測試類和函數。(注意大小寫 寫錯了會停止測試)
- 編寫測試固件 跟nose類似
會自動尋找相應的 setup_module/teardown 方法
- 可以用pytest.fixture()方法裝飾測試函數可以使用上面的固件
- 可用 pytest.mark.skipif()方法跳過測試
- 還可以用 ssh驅動分布式測試。
4.代碼覆寫率
- simply coverage來測試代碼覆寫率
- 使用指令 coverage run -m unittest 來運作測試覆寫率
- 請注意覆寫率僅僅是測試的通過覆寫率,并不是所有的if else 分支都測試了
5. 仿真與模拟
- 仿真 就是寫個跟無法獲得類相同傳回的類,模拟相關不可達傳回建立測試可能性。
- 使用模拟
- pip install Mock
- 調用MagicMock方法來模拟API傳回值 P288
6. 測試環境依賴相容
- tox 建立測試矩陣 P290
7. 文檔驅動開發
- 上文中提到的doctest 既測試的功能,又可以很好的解釋整個軟體的關鍵點。
3.小結
- unittest
- nose py.test
- 建構仿真和模拟
- 文檔(測試)驅動開發
第十一章 優化 一般分析原則與分析技術
1. 3個優化原則
- 首先要能工作——别TM改了以後用不了
- 從使用者角度考慮——使用者關注的點不是你自己關注的點
- 保持代碼的可讀性和可維護性
2. 優化政策
别瞎猜,有對應的流程:
- 先找别的東西的原因 ——資料庫 通路 I/O 等非代碼問題
- 擴充硬體 硬體比人工便宜多了…
- 編寫速度測試 就是time.time() -starttime
3. 查找瓶頸
- CPU瓶頸
- 分為 宏觀分析 微觀分析
- 宏觀分析 使用cprofile 使用指令 Python3 -m cProfile myapp.py來進行分析 還可以函數内調用 或者配合 graphviz來做更精細的使用 p302
- 微觀分析 當找到耗時較多的函數後 timeit檢視多次運作的平均時間 使用test.pystones計算精确時間 p306
- 分析記憶體使用
- 解釋并不給記憶體管理器的操作權
- 主要是要靠計數器
- ogjgraph p311-315
- 分析網絡使用情況
- ntop wireshark等
-
小結
優化的3原則、優化政策、具體的一些分析方法及工具。
第十二章 優化 一些強大的技術
代碼優化是一個疊代的過程,總的來說就是3個方法
1. 降低複雜度
- 循環複雜度 McCabe 複雜度(if while …等) 降低代碼複雜度
- 大O記法 降低函數的大O的複雜度
2. 簡化
選擇優秀的資料存儲方式
- bisect 有序清單查找為 O(logn) 注意有序性強弱
3. 使用集合子產品
- deque 連結清單 list的連結清單代替
- defaultdict 在附值和查找稍微優化相比 dict
- namedtuple 使用tupe 可以用屬性方法調用tupe元素 P326
4. 架構體系的權衡
- 是使用最佳算法 還是使用近似算法 TSP問題
- 使用隊列處理延遲問題
- 使用機率型資料結構(大機率情況優化)
5. 緩存
- 确定的緩存 注意緩存占用
- 非确定性緩存 同步性與性能取舍
- memcached 緩存服務
6. 小結
- 降低算法複雜度的方法
- 架構層權衡 提高性能
- 緩存
第十三章 并發
1. 為何需要并發
并發不是并行,處理并發可以是并行(多程序)也可以是輪詢(多線程)也可以是事務型(異步)
可以讓界面馬上可操作,可以讓I/O阻塞型任務更高效,讓部分多使用者 分發模式的任務邏輯更簡單
2. 多線程
同一個上下文環境中 不同的任務 就是一個線程
- Python中的多線程 有GIL 不能用多核加速 對I/O阻塞型事物,界面型事物有幫助,不能并行。
- python中多線程也是用一個解釋器,其開銷較多程序小
- 何時适合多線程
- 響應式頁面
- 委派式工作
- 多使用者應用程式
- 簡單串行執行個體 P345
- 簡單多程序 P347
- 線程池 Queue P348
- 雙隊列 (待處理que 和 輸出que)p351
- 處理外部錯誤 p353
- 設定限制(人為制造瓶頸)p356
3. 多程序
每個程序有自己的上下文,不受GIL影響
- 使用multiprocessing子產品 其中程序間通訊可以使用:p359
- Queue 同threading子產品的queue
- Pipe 類似套接字的使用方法 p360
- sharedctypes 共享記憶體(隻适用于ctype基本資料類型,盡量别用)
- 使用程序池 可以用 with Pool(12) as pool:的上下文管理模式來做 p362
- 可以使用 multiprocessing.dummy 中的Pool 來替代上面的真pool 進而将多程序改成多線程
4. 異步程式設計
核心就是自己控制釋放資源的時間
- 協程 非搶占型的
- async 和await async修飾的函數傳回一個future型的值(類似生成器) await方法交出程式控制權
- 異步執行個體 p371
- 無異步庫/方法時候 如何類似異步的運作方法 Executor 和 futures
- 讓所需異步運作的方法 在 Threadpoolexecutor 和 Processpoolexecutor 上面運作 然後用将結果用 await修飾 形成異步
5.小結
這裡的知識都是Python程式員應該掌握的知識。
運用多個上述方法也是正常的
第十四章 有用的設計模式
1. 建立型模式 (creational patterns)
用于生成 具有特定行為的對象
- 單例模式
- 複寫 類__new__方法 簡單易于了解 容易出錯 被繼承後有麻煩
- 複寫 metaclass 中的 __call__ 方法 常用
- 複寫 類執行個體的.__dict__方法 (博格 borg 或者說教 單态 monostate)
- 最佳實踐:**使用子產品而非類**
2. 結構型模式(structural patterns)
有助于特定用例建構代碼
- 擴充卡 duck-type
- **接口** 顯式的聲明ducktype 接口 A 依賴于接口 I B實作接口I 而不是 A依賴于B
- 使用 zope.interface 包 先定義一個interface 然後 用 @implementer(xxx)的方式來實作接口
- 抽象類ABC (Abstract Base Classes)除了跟上面差不多的接口繼承,還可以複寫 ABC類的 __subclasshook__方法來改變 isinstance的判斷條件。
- 函數注解 類型提示
- collections.abs
- 代理模式 之前十二章的那種緩存就是代理 簡單的說就是用一個更快 更節省的模式通路外部資源的方式
- 外觀(facade) 在包級别以上更加簡化其内部調用 (在包上面在包一層)
3. 行為模式(behavioral patterns)
- 觀察者模式 在一個類中維護需要操作的觀察者清單,當事件出現時候 向不同的觀察者傳遞相應的參數 p386
- 通路者模式
- 模闆
- 小結