Python code smells 例項講解

code smells

可以理解為程式碼中讓人感覺到不舒服的地方。可能是程式碼規範問題,也可能是設計上的缺陷。

很多時候一段程式碼符合基本邏輯,能夠正常執行,並不代表它是不“醜”的。程式碼中可能會存在諸如可讀性差、結構混亂、重複程式碼太多、不夠健壯等問題。

示例程式碼

上述程式碼實現了一個簡單的“員工管理系統”。

Employee 類代表公司裡的員工,有姓名、角色、假期等屬性。可以請假(),或者單獨請一天,或者以 5 天為單位將假期兌換為報酬

HourlyEmployee 和 MonthlyEmployee 分別代表以時薪或者月薪來計算工資的員工

Company 類代表公司,可以招收員工()、返回特定角色的員工列表(如 )、發放薪資等()

code smells

上面的程式碼中存在著很多可以改進的地方。

用 Enum 型別替代 str 作為員工的 role 屬性

上面的 類使用了 型別來儲存 屬性的值,比如用 代表經理,用 代表實習生。

實際上 String 過於靈活,可以擁有任何含義,用來表示角色屬性時不具有足夠清晰的指向性。不同的拼寫規則和大小寫習慣都會導致出現錯誤的指向,比如 和 , 和 。可以使用 Enum 替代 str。

修改 類中 屬性的定義:

類中 等方法也做相應的修改:

方法中使用新的 role 建立員工物件:

消除重複程式碼

類中有一個功能是返回特定角色的員工列表,即 、、 三個方法。

這三個方法實際上有著同樣的邏輯,卻分散在了三個不同的函數里。可以合併成一個方法來消除重複程式碼。

同時將 函式中的 、、 都改為如下形式:

儘量使用內建函式

上面版本中的 方法,包含了一個 迴圈。實際上該部分邏輯可以使用 Python 內建的

列表推導

來實現。

合理的使用 Python 內建函式可以使程式碼更短、更直觀,同時內建函式針對很多場景在效能上也做了一定的最佳化。

更清晰明確的變數名

舊版本:

新版本:

isinstance

當你在程式碼的任何地方看到 這個函式時,都需要特別地加以關注。它意味著程式碼中有可能存在某些有待提升的設計。

比如程式碼中的 函式:

這裡 的使用,實際上在 函式中引入了對 的子類的依賴。這種依賴導致各部分程式碼之間的職責劃分不夠清晰,耦合性變強。

方法需要與 的子類的具體實現保持同步。每新增一個新的員工型別( 的子類),此方法中的 也就必須再新增一個分支。即需要同時改動不同位置的兩部分程式碼。

可以將 的實現從 類轉移到具體的 子類中。即特定型別的員工擁有對應的報酬支付方法,公司在發薪時只需要呼叫對應員工的 方法,無需實現自己的 方法。由 引入的依賴關係從而被移除。

再把 函式中的 改為 。

由於每一個特定的 子類都需要實現 方法,更好的方式是將 實現為虛擬基類, 成為子類必須實現的虛擬方法。

Bool flag

類中的 方法有一個名為 的引數。它是布林型別,作為一個開關,來決定某個員工是請一天假,還是以 5 天為單位將假期兌換為報酬。

這個開關實際上導致了 方法包含了兩種不同的職責,只通過一個布林值來決定具體執行哪一個。

函式原本的目的就是職責的分離

。使得同一個程式碼塊中不會包含過多不同型別的任務。

因此 方法最好分割成兩個不同的方法,分別應對不同的休假方式。

Exceptions

方法中有一步 程式碼。但該部分程式碼實際上對 Exception 沒有做任何事。對於 Exception 而言:

如果需要 catch Exception,就 catch 特定型別的某個 Exception,並對其進行處理;如果不會對該 Exception 做任何處理,就不要 catch 它

在此處使用 會阻止異常向外丟擲,導致外部程式碼在呼叫 時獲取不到異常資訊。此外,使用 而不是某個特定型別的異常,會導致所有的異常資訊都被遮蔽掉,包括語法錯誤、鍵盤中斷等。

因此,去掉上述程式碼中的 。

使用自定義 Exception 替代 ValueError

是 Python 內建的在內部出現值錯誤時丟擲的異常,並不適合用在自定義的場景中。最好在程式碼中定義自己的異常型別。

最終版本

參考資料

7 Python Code Smells: Olfactory Offenses To Avoid At All Costs

https://www。starky。ltd/2021/12/06/7-python-code-smells-by-practical-example/

TAG: 程式碼方法員工子類Exception