Pythonで列挙型を定義するenum
モジュールの使い方の基本を解説します。
Contents
列挙型 enumの使い方
C/C++言語やJavaをはじめとして、多くの言語で広く見られる機能のうちの一つに列挙型があります。列挙型は、選択肢などに対して各項目を表すシンボル名で列挙して定義し、判定などに使うことができます。
例えば、曜日を表すものとして「0が日曜日、1が月曜日、…、6が土曜日」として管理することができます。しかし、どの数値が何曜日を示しているかを覚えておくのは大変ですし、もしかしたら0は月曜日かもしれません。
列挙型では「SUNDAY, MONDAY, …, SATURDAY」といったシンボル名の列挙で曜日を定義でき、背後では各シンボルが値を持っています。このように列挙型で定義しておくとプログラムの可読性が非常に向上します。
Pythonでも列挙型を使用することができ、enum
モジュールで提供されています。本記事では、enum
モジュールの基本的な使い方について紹介します。
基本的な使い方 Enum
Enumを継承した列挙型の定義
enum
モジュールを使用する際には、Enum
というクラスを継承して列挙型クラスを定義するのが基本的な使い方です。以下では、曜日を定義する例を使って説明します。
from enum import Enum class Weekday(Enum): """曜日列挙クラス""" SUNDAY = 0 MONDAY = 1 TUESDAY = 2 WEDNESDAY = 3 THURSDAY = 4 FRIDAY = 5 SATURDAY = 6 if __name__ == "__main__": print(Weekday.SUNDAY, Weekday.SUNDAY.name, Weekday.SUNDAY.value) print(Weekday.MONDAY, Weekday.MONDAY.name, Weekday.MONDAY.value) print(Weekday.TUESDAY, Weekday.TUESDAY.name, Weekday.TUESDAY.value) print(Weekday.WEDNESDAY, Weekday.WEDNESDAY.name, Weekday.WEDNESDAY.value) print(Weekday.THURSDAY, Weekday.THURSDAY.name, Weekday.THURSDAY.value) print(Weekday.FRIDAY, Weekday.FRIDAY.name, Weekday.FRIDAY.value) print(Weekday.SATURDAY, Weekday.SATURDAY.name, Weekday.SATURDAY.value) # 列挙型は判定等で使用することが可能 day_of_week = Weekday.WEDNESDAY match day_of_week: case Weekday.SUNDAY: print("日曜日") case Weekday.MONDAY: print("月曜日") case Weekday.TUESDAY: print("火曜日") case Weekday.WEDNESDAY: print("水曜日") case Weekday.THURSDAY: print("木曜日") case Weekday.FRIDAY: print("金曜日") case Weekday.SATURDAY: print("土曜日") case _: print("曜日ではありません")
【実行結果】 Weekday.SUNDAY SUNDAY 0 Weekday.MONDAY MONDAY 1 Weekday.TUESDAY TUESDAY 2 Weekday.WEDNESDAY WEDNESDAY 3 Weekday.THURSDAY THURSDAY 4 Weekday.FRIDAY FRIDAY 5 Weekday.SATURDAY SATURDAY 6 水曜日
列挙型を定義する場合には、enum
モジュールからEnum
をimport
して、Enum
を継承したクラスを作成します。今回の例ではWeekday
というクラスを作成しており「SUNDAY=0
、MONDAY=1
、…、SATURDAY=6
」のようにシンボルとなる名称と値を使って順に定義しています。
列挙型を使用する際には、Weekday.SUNDAY
のように使用することができます。列挙型の名前はWeekday.SUNDAY.name
、定義されている値はWeekday.SUNDAY.value
というようにname
やvalue
によって確認が可能です。
「day_of_week = Weekday.WEDNESDAY
」のように変数に列挙型の値を代入し、その値を使ってif
文などで判定をすることができます。上記の例では、Python 3.10からの新機能であるmatch
, case
を使ってどの曜日に該当するか判定しています。
なお、enum
の説明には直接関係ありませんが、match
, case
の使い方は、「構造的パターンマッチ(match case)の使い方」にまとめていますので興味があれば参考にしてください。
列挙型が一意であることの保証 unique
列挙型で定義した値が一意であることを保証したい場合は、以下のように@unique
というクラスデコレータを使います。
from enum import Enum, unique # 要素が一意であることを保証する場合にはuniqueデコレータをつける @unique class Weekday(Enum): """曜日列挙クラス""" SUNDAY = 0 MONDAY = 1 TUESDAY = 2 WEDNESDAY = 3 THURSDAY = 4 FRIDAY = 5 # ↓の行はエラーとなる SATURDAY = 0 if __name__ == "__main__": print(Weekday.SUNDAY, Weekday.SUNDAY.name, Weekday.SUNDAY.value) print(Weekday.MONDAY, Weekday.MONDAY.name, Weekday.MONDAY.value) print(Weekday.TUESDAY, Weekday.TUESDAY.name, Weekday.TUESDAY.value) print(Weekday.WEDNESDAY, Weekday.WEDNESDAY.name, Weekday.WEDNESDAY.value) print(Weekday.THURSDAY, Weekday.THURSDAY.name, Weekday.THURSDAY.value) print(Weekday.FRIDAY, Weekday.FRIDAY.name, Weekday.FRIDAY.value) print(Weekday.SATURDAY, Weekday.SATURDAY.name, Weekday.SATURDAY.value)
【実行結果】 Traceback (most recent call last): ...(省略)... ValueError: duplicate values found in <enum 'Weekday'>: SATURDAY -> SUNDAY
上記例はエラーとなります。具体的には、SUNDAY
とSATURDY
が共に0
という値を持つため、@unique
デコレータによりValueError
が発生します。
このように、@unique
デコレータを使用すると、列挙型の要素が一意であることを保証することができ、重複する値が存在する場合はエラーを送出します。
autoを使った自動採番
列挙型を定義する際に、定義されている数値に特に意味がある必要がないような場合には、auto
を使って自動採番することができます。
autoの基本的な使い方
auto
を使う場合には、以下の例のように各定数の定義の際にauto()
とします。
from enum import Enum, auto class Weekday(Enum): """曜日列挙クラス auto()で自動設定""" SUNDAY = auto() MONDAY = auto() TUESDAY = auto() WEDNESDAY = auto() THURSDAY = auto() FRIDAY = auto() SATURDAY = auto() if __name__ == "__main__": print(Weekday.SUNDAY, Weekday.SUNDAY.name, Weekday.SUNDAY.value) print(Weekday.MONDAY, Weekday.MONDAY.name, Weekday.MONDAY.value) print(Weekday.TUESDAY, Weekday.TUESDAY.name, Weekday.TUESDAY.value) print(Weekday.WEDNESDAY, Weekday.WEDNESDAY.name, Weekday.WEDNESDAY.value) print(Weekday.THURSDAY, Weekday.THURSDAY.name, Weekday.THURSDAY.value) print(Weekday.FRIDAY, Weekday.FRIDAY.name, Weekday.FRIDAY.value) print(Weekday.SATURDAY, Weekday.SATURDAY.name, Weekday.SATURDAY.value)
【実行結果】 Weekday.SUNDAY SUNDAY 1 Weekday.MONDAY MONDAY 2 Weekday.TUESDAY TUESDAY 3 Weekday.WEDNESDAY WEDNESDAY 4 Weekday.THURSDAY THURSDAY 5 Weekday.FRIDAY FRIDAY 6 Weekday.SATURDAY SATURDAY 7
auto()
を使うと、1から順に自動で値が設定されます。
autoで初期値を指定する方法
auto
を使う際に初期値を決めたいケースでは、先頭に初期値を設定し、以降ではauto()
を使います。
from enum import Enum, auto class Weekday(Enum): """曜日列挙クラス auto()で自動設定""" SUNDAY = 100 MONDAY = auto() TUESDAY = auto() WEDNESDAY = auto() THURSDAY = auto() FRIDAY = auto() SATURDAY = auto() if __name__ == "__main__": print(Weekday.SUNDAY, Weekday.SUNDAY.name, Weekday.SUNDAY.value) print(Weekday.MONDAY, Weekday.MONDAY.name, Weekday.MONDAY.value) print(Weekday.TUESDAY, Weekday.TUESDAY.name, Weekday.TUESDAY.value) print(Weekday.WEDNESDAY, Weekday.WEDNESDAY.name, Weekday.WEDNESDAY.value) print(Weekday.THURSDAY, Weekday.THURSDAY.name, Weekday.THURSDAY.value) print(Weekday.FRIDAY, Weekday.FRIDAY.name, Weekday.FRIDAY.value) print(Weekday.SATURDAY, Weekday.SATURDAY.name, Weekday.SATURDAY.value)
【実行結果】 Weekday.SUNDAY SUNDAY 100 Weekday.MONDAY MONDAY 101 Weekday.TUESDAY TUESDAY 102 Weekday.WEDNESDAY WEDNESDAY 103 Weekday.THURSDAY THURSDAY 104 Weekday.FRIDAY FRIDAY 105 Weekday.SATURDAY SATURDAY 106
上記結果をみると「100, 101, 102, …」のように100を初期値として、順番に数値を振ってくれていることが分かります。
数値との比較をしたい場合 IntEnum
Enum
を継承した列挙型クラスを使う場合、定義された値が数値であっても、その列挙型は通常の整数としての比較ができません。
数値と比較もできるようにしたい場合には、以下の例のようにIntEnum
を継承したクラスを使います。
from enum import Enum, IntEnum, auto class Weekday(Enum): """曜日列挙クラス Enumで定義""" SUNDAY = 0 MONDAY = auto() TUESDAY = auto() WEDNESDAY = auto() THURSDAY = auto() FRIDAY = auto() SATURDAY = auto() class WeekdayInt(IntEnum): """曜日列挙クラス IntEnumで定義""" SUNDAY = 0 MONDAY = auto() TUESDAY = auto() WEDNESDAY = auto() THURSDAY = auto() FRIDAY = auto() SATURDAY = auto() if __name__ == "__main__": # 水曜日を想定して3を設定 day_of_week = 3 # Enumを使うと数値との比較はできない if day_of_week == Weekday.WEDNESDAY: print("Enum") # IntEnumを使うと数値と比較が可能 if day_of_week == WeekdayInt.WEDNESDAY: print("IntEnum")
【実行結果】 IntEnum
上記例では、Enum
を継承したクラスとIntEnum
を継承したクラスをそれぞれ用意しています。
メインの中のday_of_week
変数は数値3として設定しています。これに対して、Enum
を継承したクラスでは直接比較しても一致と判定されませんが、IntEnum
を継承したクラスでは一致と判定されます。
数値として比較を行いたい場合は、IntEnum
を継承して列挙型を定義すると良いでしょう。
フラグとして使いたい場合 Flag
enumのもう一つの便利な使い方として、ビットフィールドのような動作を持たせる機能があります。C言語などでは、ビット演算で実装されることが多い機能ですが、enum
モジュールのFlag
を利用すると非常に読みやすく実装できます。
Flag
を使う場合には、以下の例のように使用します。
from enum import Flag, auto class Conditions(Flag): """Flagクラスを継承したフラグ""" COND_A = auto() COND_B = auto() COND_C = auto() if __name__ == "__main__": print(Conditions.COND_A.name, Conditions.COND_A.value) print(Conditions.COND_B.name, Conditions.COND_B.value) print(Conditions.COND_C.name, Conditions.COND_C.value, "\n") # AとBが設定されている状況 condition_setting = Conditions.COND_A | Conditions.COND_B print(condition_setting) # 存在の確認はinで可能 print(Conditions.COND_A in condition_setting) print(Conditions.COND_B in condition_setting) print(Conditions.COND_C in condition_setting, "\n") # 条件は|や&を使って結合できる cond_a = Conditions.COND_A | Conditions.COND_B cond_b = Conditions.COND_B | Conditions.COND_C print(cond_a | cond_b) print(cond_a & cond_b)
【実行結果】 COND_A 1 COND_B 2 COND_C 4 Conditions.COND_A|COND_B True True False Conditions.COND_A|COND_B|COND_C Conditions.COND_B
Flag
は、ビットフィールドのような動作を模倣するための列挙型を作成します。auto()
で設定していますが、値は「1, 2, 4」と2のべき乗の順に設定されます。これはビット位置を示しており、ビット演算と同じように動作します。
例えば、COND_A
とCOND_B
のフラグを一緒にセットする場合、|
演算子を使って表現できます。そして、in
キーワードを使って特定のフラグがセットされているかどうかを簡単に確認できます。
このようにFlag
を使用することで、プログラム内での設定や状態の管理を効率的に行うことができます。
まとめ
Pythonで列挙型を定義するenum
モジュールの使い方の基本を解説しました。
列挙型は、選択肢や固定のデータセットに対してシンボル名での識別や管理を可能にします。本記事では、Enum
, IntEnum
, Flag
といったクラスを継承して列挙型を定義し、判定やビットフィールドのような動作を模倣する方法などの例を紹介しました。
列挙型は、設定の有無、状態の管理、オプションの選択などのシチュエーションで有用です。このため、これらの概念と使用方法を覚えておくことは、Pythonプログラミングにおいて役立つでしょう。
enumモジュールの公式ドキュメントはこちらを参照してください。
上記で紹介しているソースコードについてはgithubにて公開しています。参考にしていただければと思います。