enum

【Python】列挙型enumの使い方の基本

【Python】列挙型enumの使い方の基本

Pythonで列挙型を定義するenumモジュールの使い方の基本を解説します。

列挙型 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モジュールからEnumimportして、Enumを継承したクラスを作成します。今回の例ではWeekdayというクラスを作成しており「SUNDAY=0MONDAY=1、…、SATURDAY=6」のようにシンボルとなる名称と値を使って順に定義しています。

列挙型を使用する際には、Weekday.SUNDAYのように使用することができます。列挙型の名前はWeekday.SUNDAY.name、定義されている値はWeekday.SUNDAY.valueというようにnamevalueによって確認が可能です。

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

上記例はエラーとなります。具体的には、SUNDAYSATURDYが共に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_ACOND_Bのフラグを一緒にセットする場合、|演算子を使って表現できます。そして、inキーワードを使って特定のフラグがセットされているかどうかを簡単に確認できます。

このようにFlagを使用することで、プログラム内での設定や状態の管理を効率的に行うことができます。

まとめ

Pythonで列挙型を定義するenumモジュールの使い方の基本を解説しました。

列挙型は、選択肢や固定のデータセットに対してシンボル名での識別や管理を可能にします。本記事では、Enum, IntEnum, Flagといったクラスを継承して列挙型を定義し、判定やビットフィールドのような動作を模倣する方法などの例を紹介しました。

列挙型は、設定の有無、状態の管理、オプションの選択などのシチュエーションで有用です。このため、これらの概念と使用方法を覚えておくことは、Pythonプログラミングにおいて役立つでしょう。

Note

enumモジュールの公式ドキュメントはこちらを参照してください。