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 名稱定義普通常數,需要時用類別分組相關常數;如果呼叫者應使用多個命名成員之一,則選擇 Enumnamedtuple() 和函式存取器是專門的替代方案,而不是強制的常數。