Python程式設計規範+最佳實踐

金色旭光發表於2024-03-18

image

前言

Python之禪是影響Python程式語言設計的19條原則,也是Python編碼規範的核心理念。

  • 優美勝於醜陋(Python 以編寫優美的程式碼為目標)
  • 明瞭勝於晦澀(優美的程式碼應當是明瞭的,命名規範,風格相似)
  • 簡潔勝於複雜(優美的程式碼應當是簡潔的,不要有複雜的內部實現)
  • 複雜勝於凌亂(如果複雜不可避免,那程式碼間也不能有難懂的關係,要保持介面簡潔)
  • 扁平勝於巢狀(優美的程式碼應當是扁平的,不能有太多的巢狀)
  • 間隔勝於緊湊(優美的程式碼有適當的間隔,不要奢望一行程式碼解決問題)
  • 可讀性很重要(優美的程式碼是可讀的)
  • 即便假借特例的實用性之名,也不可違背這些規則(這些規則至高無上)
  • 不要包容所有錯誤,除非你確定需要這樣做(精準地捕獲異常,不寫 except:pass 風格的程式碼)
  • 當存在多種可能,不要嘗試去猜測
  • 而是儘量找一種,最好是唯一一種明顯的解決方案(如果不確定,就用窮舉法)
  • 雖然這並不容易,因為你不是 Python 之父(這裡的 Dutch 是指 Guido )
  • 做也許好過不做,但不假思索就動手還不如不做(動手之前要細思量)
  • 如果你無法向人描述你的方案,那肯定不是一個好方案;反之亦然(方案測評標準)
  • 名稱空間是一種絕妙的理念,我們應當多加利用(倡導與號召)

PEP8是Python官方推出的編碼規範。本規範以PEP8 編碼規範作為出發點編寫。
編碼規範的第一原則是 提升程式碼的可讀性,如果專案是從零開始請遵守該編碼規範,如果不是,在專案和該規範出現衝突時,專案自身的規範優先。每一種規範分為強制和推薦。強制是必須遵守的規範,推薦是最佳實踐。

程式碼佈局

強制 :

  1. 統一使用4個空格縮排
  2. 單行最大長度為100
  3. 行數超過規定,建議用小括號()將多行內容連線起來,而不推薦使用反斜槓\進行連線。
  4. 不要在程式碼末尾加分號,也不要用分號將兩條命令寫在同一行
  5. 空行使用:
    1. 函式之間用兩個空行隔開
    2. 類之間用兩個空行隔開
    3. 類中方法用一個空行隔開
    4. 函式中不同邏輯程式碼塊之間可適當插入空行
  6. 空格使用:
    1. 在二元運算子兩邊都要有空格。二元運算包括:算術(+ - * / ** )、賦值(=,+=,-=)、比較( ==, <, >, !=, in, not in, is, is not)、邏輯運算(and or not)、位運算(& | ! ~ >> <<)
    2. 函式關鍵字引數=兩側不需要空格。例: res = func(name="Tom")
    3. 逗號後面要加空格,但是如果後面是小括號則不用。例:List=[1, 2, 4]
    4. 冒號前不加空格,冒號後要加空格。但是切片裡前後都不可加空格 。例:Dict = {key: value}
    5. 不要為對齊賦值語句而使用的額外空格

推薦 :

  1. import或函式存在續行的情況,遵守掛行縮排對齊
  • 第一行不應該有引數
  • 使用縮排以區分自己是續行

推薦:

def ats_import_dataset(
    db: Session = Depends(deps.get_db),
    ats_import: schemas.AtsDataSetImport,
    current_user: models.User = Depends(deps.get_current_active_user),
    controller_client: ControllerClient = Depends(deps.get_controller_client),
    background_tasks: BackgroundTasks,
 ) -> Dict:
  1. 在二元運算子之前應該換行,而不是在運算子之後換行

推薦:

income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

不推薦:

income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction - 
          student_loan_interest)

命名

強制 :

  1. 普通變數使用下劃線分隔命名法,即蛇形命名法。例: max_value
  2. 變數名和Python關鍵字衝突,在變數末尾追加下劃線。例: type_
  3. 避免使用雙下劃線開頭並結尾的名稱,該命令方式為python保留字。例:__ init__ 用於類初始化
  4. 常量用全大寫字母,用下劃線連線。例:MAX_VALUE
  5. 函式名遵循蛇形命名法。例:def get_user_info
  6. 類名使用駝峰命名法。例:class ControllerService
  7. 類中私有屬性和方法用單下劃線開頭。該定義僅為君子約定,例項化可以訪問。例:def _previate_fun()
  8. 類中不希望被繼承的變數用雙下劃線開頭。例:__user_info
  9. 模組應該用簡短全小寫的名字,如果為了提升可讀性,下劃線也是可以用的。
  10. 包使用簡短全小寫的名字,但不建議用下劃線。

推薦 :

  1. 使用列舉值、常量值替換沒有意義的數字
  2. 永遠不要使用l(小寫的L),O(大寫的O),I(大寫的I)作為單字元變數名,這些字元無法區分
  3. 描述性強。在可接受的長度範圍內,變數名所指向的內容描述越精確越好。儘量不要用那些過於寬泛的詞來作變數名 。推薦:last_login_datetime 不推薦:datetime
  4. 儘量短。命名儘量不超過5個單詞。變數名要結合程式碼情景和上下文,可以透過函式名,類名,模組名自解釋。
def get_expired_vip_users:
    pass

    # 返回過期vip使用者數
    
    # 推薦
    users_count = 0 
    
    # 不推薦
    expired_vip_users_count = 0
  1. 配型匹配
  • 使用is,has, allow等開頭來命名錶示boolean型別。例:is_vip、has_error、all_empty
  • 使用_id結尾,length/count開頭結尾的單詞標示int型別。例:user_id,host_id,max_length,users_count

函式

強制:

  1. 函式設計要儘量短小,100行是一個參考值
  2. 一個函式只做一件事,保證函式語句粒度的一致性
  3. 禁止使用可變型別作為函式引數預設值,有慘痛教訓 一個 Python Bug 幹倒了估值 1.6 億美元的公司
# 明令禁止
 List = []
 def fun(num, arr=List)
    pass

推薦 :

  1. 要將 self 作為例項方法的的第一個引數。
  2. 要將 cls 作為類靜態方法的第一個引數。
  3. 如果函式的引數名和已有的關鍵詞衝突,參考變數名和關鍵字重複的處理方法
  4. 函式圈複雜度可以衡量函式邏輯,圈複雜度不應該超過10(大概10個if else)

模組匯入

強制:

  1. 一個import語句匯入一個模組

推薦:

import os
import sys

不推薦:

import sys, os
  1. 匯入總是位於檔案的頂部,在模組註釋和文件字串之後,在模組的全域性變數與常量之前
  2. 匯入應該按照以下順序分組,每一組匯入之間加入空行:
  • 標準庫匯入
  • 相關第三方庫匯入
  • 本地應用/庫特定匯入
  1. 推薦使用絕對路徑匯入
  2. 禁止使用萬用字元的匯入,汙染名稱空間。例: from module import *

推薦 :

  1. 如果匯入的模組名較長,使用 as 給模組重新命名。例:from moudle import module_fun as mf

註釋

強制:

  1. 程式碼塊註釋使用 #, 介面註釋使用 """
  2. 塊註釋應該至少離開程式碼2個空格,#後面跟一個空格
  3. 公共函式,重要的函式必須寫介面註釋
  4. 邏輯複雜,難以理解,指令碼程式碼等情況必須要有註釋
  5. 程式碼修改要同步更新註釋

推薦:

  1. 待實現的功能用 TODO 註釋
  2. 不要用註釋描述程式碼,而是說清楚功能或邏輯

真值判斷

推薦:

  1. 對於容器型別來說,判斷空值不要用長度等於0,可以使用空序列布林值為False這個屬性來判斷。

python 中 0、None、空字串、空列表、空元組、空字典、空集合、空集合,都可以視為False,if 判斷可以視為False。其他情況都為True。
推薦:

user_list = []
if not user_list:
    pass 

if user_list:
    pass

不推薦:

user_list = [] 
if len(user_list):
    pass 
if not len(user_list): 
    pass
  1. 布林型別的判斷,推薦使用 if value
greeting = True
推薦: if greeting:
不推薦: if greeting is True:
不推薦: if greeting == True:
  1. 對空值None的判斷優先使用None為False屬性,需要顯示判斷邏輯更加清晰時可以使用 is 關鍵字。例:if value is None
  2. 空值判斷使用 if value is not None 而不是 if not value is None

錯誤捕獲

強制 :

  1. 對於無法預知錯誤型別,使用異常捕獲來保證流程正常。如網路請求
  2. 異常捕獲的顆粒度是行,對大段程式碼進行 try-catch,這是不負責任的表現
  3. 異常捕獲時分清穩定程式碼和非穩定程式碼,穩定程式碼指的是無論如何不會出錯的程式碼。
  4. 異常不要用來做流程控制,條件控制
  5. 捕獲異常是為了處理它,不要捕獲了卻什麼都不處理而拋棄之。禁止在捕獲錯誤之後使用pass不處理。如果不想處理它或不能處理,請將該異常拋給它的呼叫者。
  6. 禁止在 finally 塊中使用 return,finally 是最後執行的程式碼,return會覆蓋正常流程的return

推薦 :

  1. 存在if else 巢狀複雜的邏輯時,使用try except最佳化
  2. 可以透過預先檢查進行規避時,推薦不要異常捕獲來處理
  3. 錯誤的返回推薦異常類而不是變數

工程結構

推薦 :
Python專案沒有其他語言如Java的Maven來構造固定的工程檔案目錄,結合Python社群推薦的工程目錄制定推薦結構如下:

sample_project
 ├── readme.md
 ├── docs
 │   ├── api.yml
 │   └── public_read.md
 ├── requirements.txt
 ├── app
 │   ├── __init__.py
 │   ├── core.py
 │   └── helpers.py
 ├── config
 │   ├── mysql.py
 ├── deploy
 │   ├── __init__.py
 │   └── run.sh
 ├── db
 │ 
 ├── utils
 │ 
 └── tests
     ├── __init__.py
     └── test_basic.py

readme.md: 專案說明檔案
docs: 存放專案文件,包括功能詳細說明,api docs等
requirements.txt: 存放軟體依賴的外部Python包列表
app: 存放專案介面主要程式碼的目錄
config: 存放專案配置檔案,如mysql,redis等配置項
deploy:存放部署檔案,部署指令碼等。如docker-compse.yml檔案
db: 存放資料庫相關程式碼
utils: 存放專案工具程式碼,通常和業務無關
tests: 存放單元測試檔案

相關文章