「Enum便利だぞ」と伝えるための記事。
その1. 基本
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
>>> Color.RED
<Color.RED: 1>
>>> Color.RED.value
1
>>> Color.RED.name
'RED'
# 値からインスタンス化できる
>>> Color(1)
<Color.RED: 1>
# メンバーの値に合致しなければValueError
>>> Color(4)
...
ValueError: 4 is not a valid Color
# イテレート
>>> for member in Color:
>>> print(member.name, member.value)
RED 1
GREEN 2
BLUE 3
ちなみにメンバー変数は大文字で定義することが強く推奨されている。
列挙型 HOWTO — Python 3.12.1 ドキュメント
注釈: Enumメンバーは大文字/小文字?
列挙型は定数を表すために使われるため、また mixin クラスのメソッドや属性との名前の衝突の問題を回避するため、メンバには UPPER_CASE の名前を使うことが強く推奨されており、例でもこのスタイルを用います。
その2&3. IntEnum/StrEnum
基本のEnum型がそれぞれint, str型を継承したクラス。
派生列挙型と呼ばれている。
地味に思うかもしれないが、これが結構便利。
int, strそれぞれの型を前提とした値の比較やメソッドを利用することができる。
以下「# 値の比較」で示す例はenum.Enum
クラスの場合、正しく動作しない。
IntEnum
from enum import IntEnum
class Weekday(IntEnum):
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 3
THURSDAY = 4
FRIDAY = 5
SATURDAY = 6
SUNDAY = 7
# 値の比較(数値として扱える)
>>> dow = 1
>>> dow == Weekday.MONDAY
True
>>> dow == Weekday.TUESDAY
False
>>> dow >= Weekday.WEDNESDAY
False
StrEnum
from enum import StrEnum
class Locale(StrEnum):
JAPANESE = "ja"
ENGLISH = "en"
SPANISH = "es"
HINDI = "hi"
# 値の比較 (文字列として扱える)
>>> locale = "ja"
>>> locale == Locale.JAPANESE
True
>>> locale == Locale.ENGLISH
False
>>> "j" in Locale.JAPANESE
True
>>> locale.startswith(Locale.JAPANESE)
True
その4. Flag
あまり使われるケースは少ないかもしれないが、EnumのサブクラスにFlag型がある。
Flag型の特徴は、メンバー変数同士の集合和(ビット演算子 |
)を用いた状態管理を行うことができる点である。
例として、会員サイトのユーザーステータスを状態するケースを考えてみる。
ユーザーステータスを状態遷移のように定義することで「有効or無効」ステータスをフラグ管理(True/False)しなくて済むメリットが生まれる。
from enum import Flag, auto
class UserStatus(Flag):
"""
ユーザーのステータスを管理する値を想定する
1. PROVISIONAL: 仮登録
2. REGISTERED: 登録済み
3. BANNED: 停止中
4. LEFT: 退会済み
"""
PROVISIONAL = auto()
REGISTERED = auto()
BANNED = auto()
LEFT = auto()
# ビット演算子 `|` を用いて「有効でない」会員ステータスを定義する
INACTIVE = PROVISIONAL | BANNED | LEFT
# 裏を返せば:
ACTIVE = REGISTERED
# 仮登録のユーザーは「INACTIVEであり」「ACTIVEではない」
>>> status = UserStatus.PROVISIONAL
>>> status in UserStatus.INACTIVE
True
>>> status in UserStatus.ACTIVE
False
# 登録中のユーザーは「INACTIVEではなく」「ACTIVEである」
>>> status = UserStatus.REGISTERED
>>> status in UserStatus.INACTIVE
False
>>> status in UserStatus.ACTIVE
True
Flag型ではメンバーの値にauto()
を使うことが推奨されている。
auto()
で生成される実際の値は2のべき乗の値となる。これによってビット演算が可能となる。
for m in UserStatus:
print(m.name, m.value)
PROVISIONAL 1
REGISTERED 2
BANNED 4
LEFT 8
>>> UserStatus.INACTIVE
<UserStatus.INACTIVE: 13>
>>> UserStatus.INACTIVE & UserStatus.BANNED
4
"""
Flag型の in 演算子はビット演算子による
(UserStatus.INACTIVE & UserStatus.BANNED) == UserStatus.BANNED
... 13 & 4 == 4
... 4 == 4
=> True
"""