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文などで判定をすることができます。上記の例では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となります。
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を継承した列挙型クラスを使う場合、定義自体は数値になっていたとしてもIntの数値と直接比較することはできません。
数値と比較もできるようにしたい場合には、以下の例のように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の方で「day_of_week = WeekdayInt.WEDNESDAY」のように変数に列挙型の値を代入して判定することも可能です。数値でも判定で使えるようにしたい場合は、IntEnumを継承して列挙型を定義するようにしましょう。
フラグとして使いたい場合 Flag
enumのもう一つの便利な使い方として設定フラグのOn/Offを管理し、簡単に判定できるようにする例があります。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
基本的にこれまでのEnumやIntEnumと使い方は同じです。Flagを継承した列挙型のクラスを定義します。
auto()で設定していますが順番に1, 2, 4という値が設定されていることが分かります。2進数で考えると「00000001」「00000010」「00000100」となっているので、C言語等でフラグをビット演算をするときと同じですね。
例えば、COND_AとCOND_Bが設定されている状態は「|」演算を使うことで表現できます。そして、inキーワードを使うことで当該設定が含まれていることが簡単に判定できます。
このようにすることで、プログラムにおいて設定の有無を簡単に判定することができます。また、「cond_a | cond_b」や「cond_a & cond_b」のように「|」や「&」で条件を簡単に結合できる点も便利です。
まとめ
Pythonで列挙型を定義するenumモジュールの使い方の基本を解説しました。
列挙型は、選択肢等のデータに対して各項目を表すシンボル名で列挙して定義し、判定等に使うことができます。本記事では、Enum, IntEnum, Flagといったクラスを継承して列挙型を定義し、判定などに使う例を紹介しました。
列挙型は、設定を表現する際などによく使う方法なので使い方を覚えてもらえると便利かなと思います。
enumモジュールの公式ドキュメントはこちらを参照してください。