第 33 課 · 進階 · 18 分鐘

Python 列舉

為一組固定值命名一次,然後在所有地方使用這些名字。

列舉Enum 成員尋找走訪

有人在杯型欄裡打了「中懷」,查價直接落空。如果一開始就只允許固定的幾個名字,這個錯誤在門口就會被攔下。為什麼 Enum 比裸字串安全?

Enum 為一組固定的命名值提供真正的型別。成員是唯一物件,比到處傳遞裸字串更容易比較和驗證。需要顯示或序列化時,還能分別取得名稱和值。 你還會把 auto 值留在內部、組合權限,並驗證儲存值。

定義命名成員

繼承 Enum,並給名字賦值。Color.RED 是成員物件,不是字串 "red"。用 .name 取得標識符,用 .value 取得底層值。成員之間按身份比較。

from enum import Enum

class Color(Enum):
    RED = "red"
    GREEN = "green"

print(Color.RED.value)
print(Color.RED.name)

有穩定值的名字

走訪成員

Enum 類可以按定義順序走訪。它適合建構選單、驗證選項和生成文件。走訪產生的是成員物件,不是原始值。

from enum import Enum

class Color(Enum):
    RED = "red"
    GREEN = "green"

for color in Color:
    print(color.name, color.value)

一組固定的選擇

按值或按名稱尋找

用值呼叫列舉會回傳對應成員,找不到時拋出 ValueError。方括號按成員名稱尋找,名稱不存在時拋出 KeyError。根據輸入來源選擇正確的尋找方式。

from enum import Enum

class Color(Enum):
    RED = "red"
    GREEN = "green"

print(Color("green"))
print(Color["RED"])

兩種尋找方式

auto() 的值只是實現細節

auto() 會分配連續整數,程式內部用很方便,但不適合持久化。插入新成員時這些數字可能改變。@unique 能抓住意外別名,也就是兩個名字拿到同一個值的情況。

from enum import Enum, auto, unique

@unique
class Stage(Enum):
    NEW = auto()
    PAID = auto()
    SHIPPED = auto()

print([(stage.name, stage.value) for stage in Stage])
print(Stage.PAID is Stage(2))

名字穩定,auto 數字只在本程式內有效

用 Flag 組合權限

Flag 成員是可以按位組合的位值。檢查權限時,用按位與再和該權限比較,不要直接比較整個組合值。只有在邊界格式有明確文件時,才儲存組合後的整數。

from enum import Flag, auto

class Permission(Flag):
    READ = auto()
    WRITE = auto()
    ADMIN = READ | WRITE

editor = Permission.READ | Permission.WRITE
print(editor)
print((editor & Permission.READ) == Permission.READ)
print((editor & Permission.ADMIN) == Permission.ADMIN)

小型權限集合用位標誌

存值,讀回時再驗證

Enum 成員不是 JSON 值。在邊界存 member.value,讀回時呼叫列舉類,既驗證又還原成員。未知值會拋出 ValueError,這正是我們想要的失敗,而不是悄悄接受拼字錯誤。

from enum import Enum

class Plan(Enum):
    FREE = "free"
    PRO = "pro"

payload = {"plan": Plan.PRO.value}
restored = Plan(payload["plan"])
print(restored.name, restored.value)
try:
    Plan("team")
except ValueError as error:
    print(type(error).__name__, error)

儲存用值,程式碼裡用成員

本課要點

  • Enum 成員是帶名稱和值的唯一物件。
  • 用 .name 和 .value 分別存取名稱和值。
  • 走訪會按定義順序產生成員。
  • Color(value) 按值尋找;Color[name] 按名稱尋找。
  • 無效尋找會按方式不同拋出 ValueError 或 KeyError。
  • 不要持久化 auto 值;組合狀態用 Flag;讀回儲存值時讓列舉類驗證。

常見錯誤

  • 把列舉成員和它的原始值直接比較,並期待相等。
  • 需要封閉選項集合時,仍然使用裸字串。
  • 混淆按值尋找和按名稱尋找。
  • 把 Enum 成員直接寫進 JSON,以為它會自動變成對應的值。
輪到你

定義帶 RED 和 GREEN 的 Color 列舉,列印 Color.RED.value,再尋找 Color('green')。

動手試試

改改變數、先預測輸出,再執行程式碼。第一次點擊「執行程式碼」時會下載 Python 執行時,可能需要幾秒鐘。

瀏覽器內 Python本機執行 · 無需帳號
首次執行會從 CDN 下載 Pyodide。修改後點擊「執行程式碼」。

練習本課

12 道題,作答後立即看到解釋。

查看 12 道題答案開始練習
← functoolsCSV 檔案 →