天天看點

[Python] Python工匠(Github)

1、善用變量來改變代碼品質

變量命名

變量要有描述性,不能太寬泛

bad:day, host, cards, temp

good:day_of_week, hosts_to_reboot, expired_cards 

變量名最好讓人猜出類型

python是動态語言,沒有變量類型聲明,隻能根據上下文猜測

boolean:is_superuser, has_error, allow_vip

int/float:user_id, user_count, number_of_apples

适當使用“匈牙利命名法”

把變量縮寫放在變量名的最前面

students指向一個包含person對象的list

students->pl_students

變量名盡量短但不要太短

兩三個單詞左右

避免隻有一兩個字母的短名字

其他

同一段代碼内不要使用過于相似的變量名,如user1, user2, user3

不要使用帶否定含義的變量名,is_not_normal->is_special

變量使用 

保持一緻性

不要用一個變量名一會表示str,一會表示list

盡量不要用globals()/locals()

變量定義盡量靠近使用

合理使用namedtuple/dict讓函數傳回多個值

控制單個函數内的變量數量

及時删掉沒用的變量

需要的時候再定義變量

2、編寫條件分支代碼的技巧

最佳實踐

避免多層分支嵌套

過多的層次縮進影響代碼可讀性

用return/raise提前結束分支

封裝過于複雜的邏輯判斷

過多的not/and/or影響代碼可讀性

用函數封裝具體判斷

留意不同分支下的重複代碼

重複代碼令代碼使用者難以區分不同分支的差別

利用python的動态特性改善代碼

謹慎使用三元表達式

用 x and a or b 模拟

用 if/else 替換

隻用三元表達式處理簡單的邏輯分支

常見技巧

使用“的摩根定律”

not a or not b -> not( a and b )

自定義對象的“布爾真假”

python對象具有布爾值,适當利用可簡化分支代碼

魔法方法(user-defined method)__bool__和__len__

條件判斷中使用all()/any()

all(seq):僅當seq中所有對象為真時傳回true

any(seq):隻要seq中有對象為真就傳回true

使用try/while/for中的else分支

常見陷阱

與none值的比較

留意and和or的運算優先級

3、使用數字與字元串的技巧

序言

python中的三種資料類型:整型(int)、浮點型(float)和複數(complex)

python中的整型不區分有無符号,且永不溢出

少寫數字字面量(integer literal),即那些直接出現在代碼裡的數字,對于會重複出現的數字,可利用枚舉類型enum定義

少用裸字元串處理,即隻用基本運算操作字元串,可用對象化的方式建構和編輯,如sqlalchemy、lxml、json

不必預計算字面量表達式,python解釋器會自動預先計算

實用技巧

布爾值其實也是“數字”,如 true+1=2

改善超長字元串的可讀性,使用\或+拆分,或用()将長字元串包起來

在多級縮進裡插入多行字元串時,可用textwrap調整縮進

别忘了“r”開頭的内建字元串函數(從右往左)

使用無窮大float("int")和float("-int")

常見誤區

value+=1 并非線程安全,被python解釋器執行時,不是原子操作

字元串拼接(+=)并不慢

4、容器的門道

底層看容器

python常用的内建容器:清單(list)、元組(tuple)、字典(dict)、集合(set)

避免頻繁擴充/建立新清單,清單記憶體是按需配置設定的,現有記憶體不夠時會觸發擴容操作

多使用yield關鍵字,傳回生成器對象

盡量使用生成器表達式替代清單推導表達式

盡量使用子產品提供的懶惰對象

在清單頭部操作多的場景使用deque子產品

使用集合/字典判斷成員是否存在

高層看容器

python是“鴨子類型”語言,隻某對象滿足了該類型的接口規範,就可以被當做該類型的對象使用

各個容器類型實作的接口協定定義了容器,不同的容器是“是否可疊代”、“是否可修改”、“有沒有長度”等各種特性的組合

寫代碼時應更多關注容器的抽象屬性,而非容器類型本身

面向接口而非具體實作程式設計

常用技巧

使用元組改善分支代碼:二分查找子產品bisect

在更多地方使用動态解包:使用*或**将可疊代對象“解開”

最好不用“擷取許可”,也無需“要求原諒”:使用collections.defaultdict,而非捕捉異常

使用next()函數:接收一個疊代器作為參數,傳回疊代器的下一個元素,配合生成器使用

使用有序字典去重:collections.ordereddict

當心已經枯竭的疊代器:周遊完後再周遊,就沒有結果了

别在循環體内修改被疊代對象:周遊的同時修改會出錯,應使用一個空清單儲存結果,或使用yield傳回生成器

5、讓函數傳回結果的技巧

程式設計建議

單個函數不要傳回多種類型(單一職責)

使用partial構造新函數

抛出異常,而不是傳回結果與錯誤

謹慎使用none傳回值,僅在以下情況使用

作為操作類函數的預設傳回值

作為某些“意料之中”的可能沒有的值

作為調用失敗時代表“錯誤結果”的值,函數簽名(名稱與參數)與none傳回值之間是否存在一種“意料之中”的暗示

合理使用“空對象模式”,即使用一個符合正常結果接口的“空類型”來代替空值傳回/抛出異常,以降低調用方處理結果的成本

使用生成器函數代替傳回清單

限制遞歸的使用,python對遞歸支援有限,盡量采用循環實作 

6、異常處理的三個好習慣

異常處理工作由“捕獲”和“抛出”兩部分組成

三個建議

隻做最精确的異常捕獲

永遠隻捕捉可能會抛出異常的語句塊

盡量隻捕獲精确的異常類型,而不是模糊的exception

别讓異常破壞抽象一緻性

讓子產品隻抛出與目前抽象層級一緻的異常

在必要的地方進行異常包裝與轉換

異常處理不應喧賓奪主 

使用上下文管理器(context manager)

7、編寫道地循環的兩個建議

for <item> in <iterator> 和 while <condition>

使用函數修飾被疊代對象來優化循環

使用product扁平化多層嵌套循環

使用islice實作循環内隔行處理,islice(seq, start, end, step)

使用takewhile替代break語句,takewhile(predicate, iterable)

使用生成器編寫自己的修飾函數

按職責拆解循環内的複雜代碼塊

複雜循環體如何應對新需求,避免循環體内的代碼膨脹

使用生成器函數解耦循環體,隔離不同職責的代碼塊

8、裝飾器使用技巧

裝飾器(decorator)可以在函數外部修改函數

嘗試用類來實作裝飾器

常見錯誤

“裝飾器”并不是“裝飾器模式”

用functools.wraps()裝飾内層函數

修改外層變量使用nonlocal

9、一個關于子產品的小故事

module是用來組織python代碼的基本機關

合理的子產品結構與分層非常重要

整個項目内的子產品間依賴關系流向,應該是單向的,不能有環形依賴存在

10、做一個精通規則的玩家

11、高效操作檔案的三個建議

使用pathlib子產品

掌握如何流式讀取大檔案

設計接收檔案對象的函數

12、寫好面向對象代碼的原則

python對oop的支援

沒有嚴格的類私有成員

沒有接口(interface)對象

solid設計原則

s(單一職責原則):一個類應該隻有一種被修改的原因

o(開放-關閉原則):類應該對改動關閉,對擴充開放

l(李氏替換原則):子類應該可以任意替換父類被使用

d(依賴倒置原則):高層子產品不應依賴低層子產品,二者都應依賴于抽象

i(接口隔離原則):客戶應該不依賴于他不使用的方法