碼住!12 種生產級 Python 代碼風格
在工作中,我們編寫的代碼盡可能地易于理解,這意味著:
- 變量名有意義且更長(而不是 a、b 和 c)
- 函數名稱有意義且更長
- 大量注釋和文檔來解釋代碼
- 類型提示無處不在
- 字符串似乎更長、更冗長
- 等等等等
因此我們需要不斷精進自己的編碼能力,以適應更多的要求。
下面就跟大家分享一下以下堪稱一絕的生產級 Python 代碼風格。
1 使用括號解包元組
以下是一些常規元組的解包:
圖片
在生產級代碼中,我們通常不使用諸如 a 或 b
因此,我們可以使用括號來幫助元組解包,如下所示:
圖片
請注意,通過這種方式,我們的元組解包可以容納更長(且更具描述性)的變量名。
一個工作中的例子:
圖片
2 多行列表推導式
正常的列表推導式如下所示:
圖片
在生產代碼中,我們通常不使用變量名 i
我將如何重寫上述代碼:
圖片
一個工作中的例子:
圖片
3 使用括號組合字符串
生產級字符串通常太冗長,無法放在一行中。因此我們使用括號將它們組合起來。
圖片
注意:在括號內,字符串文字(使用引號)會自動相加,我們不需要使用 + 運算符來執行此操作
4 使用括號進行多行方法鏈接
正常方法鏈接:
圖片
在生產級代碼中,方法名稱大多數時候都更長,并且我們將更多方法鏈接在一起。
我們使用括號將所有這些內容分成多行,而不是縮短任何方法名稱或變量名稱。
圖片
請注意,如果我們在括號內進行方法鏈接,則不需要使用 \ 字符進行顯式換行
5 索引嵌套字典
索引嵌套字典的正常方式:
圖片
這里存在一些問題:
- 生產級代碼中的字典有更多嵌套層級
- 字典鍵的名稱更長
- 我們通常無法將整個嵌套索引代碼壓縮到一行中。
因此,我們將其分成多行,如下所示:
圖片
如果這還不夠的話,我們將索引代碼分成更多行:
圖片
或者如果我們仍然發現這很難閱讀,我們可以這樣做:
圖片
6 編寫可讀且信息豐富的函數
通常,新手是這樣編寫函數的:
圖片
包含此類代碼的 PR 很可能會被拒絕
- 函數名稱不具描述性
- 參數變量名不好
- 沒有類型提示,所以我們乍一看不知道每個參數應該是什么數據類型
- 沒有類型提示,所以我們也不知道函數應該返回什么
- 沒有文檔字符串,所以我們必須推斷函數的作用
以下是我們在生產級 Python 代碼中編寫函數的方法
圖片
- 函數名稱應該具有描述性
- 參數名稱應該更具描述性,而不是例如 a、b、c
- 每個參數都應該有一個類型提示
- 函數的返回類型也應該包括在內
- 詳細說明函數功能、函數所接受的參數及其輸出的文檔字符串應以三重引號中的字符串形式包含在內。
7 盡可能減少縮進級別
這是一個 for 循環。如果條件滿足,我們就執行某些操作。
圖片
一些同事和高級工程師可能實際上會對這段代碼有所不滿——通過減少縮進級別可以寫得更好。
我們重寫這個代碼,同時減少do_something()的縮進級別。
圖片
請注意, do_something()的縮進級別已減少了 1 級,只需使用 if not condition而不是if condition。
在生產級代碼中,縮進級別可能會更多,如果縮進太多,我們的代碼就會變得煩人且難以閱讀。因此,這個技巧可以讓我們的代碼更整潔、更易于閱讀,
8 帶括號的布爾條件
這是一個使用and關鍵字連接起來的包含 3 個條件的 if 語句。
圖片
在生產級代碼中,條件會變得更長,并且可能會有更多條件。因此,我們可以解決這個問題的一種方法是將這個巨大的條件重構為一個函數。
或者,如果我們認為沒有必要為此編寫新函數,則可以使用括號編寫條件語句。
圖片
這樣,我們就不會被迫為這一個條件語句編寫一個新的函數或變量,同時我們還能保持它的整潔和可讀性。
有時我可能實際上更喜歡這樣寫,盡管這只是基于個人喜好:
圖片
9 防止 None 值
訪問對象某些嵌套屬性的普通代碼。
圖片
此代碼中的一些問題可能會導致我們的 PR 被拒絕:
- 如果dog為 None,我們會收到錯誤
- 如果dog.owner為 None,我們也會收到錯誤
- 本質上,這段代碼不能防止dog或dog.owner為 None 的可能性。
在生產級代碼中,我們需要積極防范此類情況。下面是我重寫此代碼的方法。
圖片
Python 中的 and 和 or
- 如果 dog 為 None,我們的表達式終止于 “if dog
- 如果 dog 不為 None,但 dog.owner 為 None,則表達式終止于“ if dog and dog.owner
- 如果我們沒有任何 None 值,則可以成功訪問dog.owner.name,并將其與字符串“bob”進行比較
這樣,我們就有了額外的保護,防止dog或dog.owner為 None 值的可能性。
10 防止迭代 None 值
以下是我們如何迭代某些可迭代對象(例如列表、字典、元組等)
圖片
這樣做的一個問題是,它不能防止 mylist 為 None —— 如果 mylist
以下是我對這段代碼的改進:
圖片
表達式 “mylist or None”
- 如果 mylist
- 如果 mylist
因此,如果 mylist 為 None,則表達式 “mylist or None”
11 內部函數以 _ 開頭
這是一個示例類。在這里,run方法使用其他方法 clean 和 transform
圖片
在生產級代碼中,我們希望盡可能明確,因此嘗試區分內部和外部方法。
- 外部方法——供其他類和對象使用的方法
- 內部方法——類本身使用的方法
按照慣例,內部方法以下劃線 _ 開頭是一種很好的做法
如果我們重寫上面的代碼,我們會得到:
圖片
注意:在方法名前面添加下劃線并不會將其隱藏在其他類和對象中。事實上,功能上沒有區別。
12 常用功能裝飾器
這是一個包含 3 個函數的類,每個函數執行不同的操作。但是,請注意,不同函數之間的步驟類似 — try-except 塊以及日志記錄功能。
圖片
減少重復代碼量的一個很好的做法是編寫一個包含常用功能的裝飾函數。
圖片
這樣,如果我們想要更新公共代碼(try-except 和日志代碼),我們不再需要在 3 個地方更新它們 - 我們只需要更新包含公共功能的裝飾器代碼。