17
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

PythonAdvent Calendar 2023

Day 14

【Python】Enumって4種類あんねん

Last updated at Posted at 2023-12-14

「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
"""
17
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?