HOWTO · Python
Python 中的常數
瞭解如何使用 UPPER_CASE 名稱定義 Python 常數、分組相關值,以及在需要固定成員集合時使用 Enum。
本頁內容
Python 沒有能讓名稱永久唯讀的 const 宣告。通常的做法是在模組層級定義一個 UPPER_CASE 名稱,並把重新指定視為程式碼審查錯誤。可以使用類別分組相關值;如果值是固定的命名成員集合,應使用 Enum,避免呼叫者替換成任意成員。
使用 UPPER_CASE 名稱定義模組層級常數
把模組或套件共享的值放在模組頂部附近,並使用大寫字母和底線命名。其他模組可以正常匯入這些名稱。這個慣例表達意圖,但 Python 仍允許重新繫結名稱。
TIMEOUT_SECONDS = 30
SUPPORTED_FORMATS = ("json", "toml")
print(TIMEOUT_SECONDS)
TIMEOUT_SECONDS = 60 # Legal Python, but a reassignment of a constant by convention.
print(TIMEOUT_SECONDS)
輸出:
30
60
元組可以防止修改該元組物件,但不能讓模組名稱不可變:SUPPORTED_FORMATS 仍可重新繫結。列表等可變值也能被原地修改。實際可行時選擇不可變值,並把屬於模組公共設定或 API 的常數放在模組中。
使用類別分組相關值
類別可以為相關值提供可讀的命名空間,例如透過 Settings.DEFAULT_TIMEOUT 存取。這有助於組織程式碼,但普通類別屬性不是語言強制的常數;類別本身以及通常的實例都可以被修改。
class Settings:
DEFAULT_TIMEOUT = 30
RETRY_LIMIT = 3
print(Settings.DEFAULT_TIMEOUT)
Settings.DEFAULT_TIMEOUT = 60
print(Settings.DEFAULT_TIMEOUT)
輸出:
30
60
自訂描述器或元類別可以拒絕某些指定,但會增加複雜度,還必須處理實例屬性、子類別化和其他修改路徑。不要因為普通類別看似能防止重新指定就使用它;除非有明確且經過測試的強制需求,否則只把它當作命名空間。
使用 Enum 表示固定的命名值集合
當值必須屬於定義好的成員集合時,使用標準函式庫的 enum 模組。透過類別存取 Enum 成員,嘗試賦予新的成員值會引發例外。當集合本身具有狀態、色彩或模式等領域含義時,Enum 比大寫變數更合適。
from enum import Enum
class Color(Enum):
RED = "red"
BLUE = "blue"
print(Color.RED.value)
try:
Color.RED = "green"
except AttributeError as error:
print(type(error).__name__)
輸出:
red
AttributeError
Enum 不會讓程式中的所有值都不可變,也不能與原始字串互換。成員身分重要時使用 Color.RED,API 需要底層值時使用 Color.RED.value。解析外部輸入時建構 Color("red");未知值會引發 ValueError,可在邊界明確處理。
將 namedtuple 和函式作為替代方案
較舊的 collections.namedtuple() 技術可以分組不可變欄位。Constants = namedtuple("Constants", "PI E") 後執行 values = Constants(3.14159, 2.71828),會建立不能給欄位指定值的元組。但它不能阻止重新繫結 values,對於命名選項也不如 Enum 有表達力。
回傳值的函式可以隱藏建構過程或按需計算,但它不是常數宣告。例如 def api_version(): return "v1" 表示存取器,而不是不可變繫結。簡單常數優先使用模組層級名稱,分組使用類別,封閉集合使用 Enum。
選擇並驗證模式
普通共享值使用 UPPER_CASE 模組名稱,需要命名空間改善組織時使用類別,固定領域集合使用 Enum。始終說明修改邊界:普通名稱和類別屬性可以重新繫結,而 enum 成員拒絕重新指定。上述範例驗證了成功存取以及重要的失敗或重新指定行為。程式碼審查、測試和清晰的 API 仍是輕量慣例的重要組成部分。
摘要
Python 沒有內建的 const 關鍵字。使用模組層級 UPPER_CASE 名稱定義普通常數,需要時用類別分組相關常數;如果呼叫者應使用多個命名成員之一,則選擇 Enum。namedtuple() 和函式存取器是專門的替代方案,而不是強制的常數。