enum 超まとめ python3.11

> ## 追記
> 本記事は当初python3.6 のenumについて書きました。
> - python3.7 からの機能は以下の通りです、大して変わってないです。
> https://qiita.com/ksato9700/items/35a0bdc04693b3b09757#enum
> - [3.8](https://docs.python.org/3.8/whatsnew/3.8.html), [3.9](https://docs.python.org/3/whatsnew/3.9.html), [3.10](https://docs.python.org/3/whatsnew/3.10.html) が出ましたが、enumについては変更ないです。
> - [3.11](https://docs.python.org/3/whatsnew/3.11.html) は色々更新がありました。str型と比較できるStrEnumの追加や、制約を検証できるverify()デコレータの追加が大きいです。

# 前置き

enum の機能自体は3.4からありますが3.6になってから追加機能が加えられています。
そこで改めてpython のenum全体をざらっとまとめる記事を書いてみたいと思いました。

- 参考: 公式ドキュメント https://docs.python.jp/3/library/enum.html

# 目次
- enum概要
- 機能
  - Enum クラス
  - IntEnum クラス
  - auto 関数  ※python3.6から
  - Flag クラス ※python3.6から
  - IntFlag クラス ※python3.6から
- まとめ

# enum概要
enumはenumerationの略で列挙型とも言われ、多くの言語で採用されている。
enumの概要や各言語のenumの簡単な例はwikipedia でまとまってたのでenum自体初めてという人参照するとよいかも。
https://ja.wikipedia.org/wiki/%E5%88%97%E6%8C%99%E5%9E%8B



# 機能

## Enum クラス

最初は最も基本的なenumであるEnumクラス。説明するよりコードを見たほうが早い。

```python
from enum import Enum


class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3


def print_color(color):
    if color == Color.RED:
        print('Color is red')
    elif color == Color.GREEN:
        print('Color is green')
    elif color == Color.BLUE:
        print('Color is blue')
    else:
        print('not Color enum')


if __name__ == '__main__':
    print_color(Color.BLUE)  # Color is blue
    print_color(1)  # not Color enum
    print(Color)  # <enum 'Color'>
    print(Color(1))  # Color.RED
    print(Color.RED == Color.RED)  # True
    print(Color.RED == Color.GREEN)  # False
    for color in Color:
        print(color)  # Color.RED\nColor.GREEN\nColor.BLUE
```

注目ポイントは `Color(1)` と数字で指定するとそれに対応した値(Color.RED)が返ってくることである。
しかし、 `print_color(1)` とすると、 `print('not Color enum')` が実行されることから、 **int型とenumの比較はできない仕様になってい** ることが分かる。
また、 `for color in Color:` のように for文で回せる。定義順(RED -> GREEN -> BLUE)で回る。


## IntEnum クラス

IntEnumクラスはEnum クラスとintのサブクラスになっている。IntEnumのメンバーは **int型と比較することができる。** 違いはそれだけ。


```python
from enum import IntEnum


class Color(IntEnum):
    RED = 1
    GREEN = 2
    BLUE = 3


if __name__ == '__main__':
    print_color(Color.BLUE)  # Color is blue
    print_color(1)  # Color is red
    print(int(Color.RED))  # 1
```

`print_color` 関数は前回と同じなので省略。


## auto 関数
python3.6で追加された機能。
これまでRED, GREEN, BLUE などのメンバーに直接値を指定していたが、これを自動で値を割り振るのが `auto` 関数である。Enum, IntEnum, Flag, IntFlag のどれにも適用可能。

```python
from enum import IntEnum, auto


class Color(IntEnum):
    RED = auto()
    GREEN = auto()
    BLUE = auto()


if __name__ == '__main__':
    print(int(Color.RED))  # 1
    print(int(Color.GREEN))  # 2
    print(int(Color.BLUE))  # 3
```

enumのメンバーの実際の値が何かというのは関心がないことが普通だ。そういった場合は問答無用で `auto` 関数を使うべきだと思う。
ちなみにこの場合、RED だけ `RED = 10` にして、残りは `auto()` にすると、10からの連番がふられるようになる。

## Flag クラス
python3.6で追加された機能。
Enumクラスの機能に加え、 Flag メンバーはビット演算子 (&, |, ^, ~) を使って組み合わせられる。
メンバの値には `auto` 関数を使うことが推奨されている。


```python
from enum import Flag, auto


class Color(Flag):
    RED = auto()
    GREEN = auto()
    BLUE = auto()
    PURPLE = RED | BLUE
    WHITE = RED | GREEN | BLUE


def print_color(color):
    if color == Color.RED:
        print('Color is red')
    elif color == Color.GREEN:
        print('Color is green')
    elif color == Color.BLUE:
        print('Color is blue')
    elif color == Color.PURPLE:
        print('Color is purple')
    elif color == Color.WHITE:
        print('Color is white')
    else:
        print('not defined')


if __name__ == '__main__':
    print_color(Color.BLUE)  # Color is blue
    print_color(Color.PURPLE)  # Color is purple
    print_color(Color.RED | Color.BLUE)  # Color is purple
    print_color(Color.RED | Color.GREEN)  # not defined
    print_color(Color.WHITE)  # Color is white
    print_color(Color.RED | Color.GREEN | Color.BLUE)  # Color is white
```

このような使い方が可能になる。使ったことがない(使うシーンが思いつかない)ので実用性はあまりわからない。


## IntFlag クラス
python3.6で追加された機能。
Flag の int 版である。IntEnumと同じく、intを継承している。IntEnumと同様に **int型と比較することができる。**


```python
from enum import IntFlag, auto


class Color(IntFlag):
    RED = auto()
    GREEN = auto()
    BLUE = auto()
    PURPLE = RED | BLUE
    WHITE = RED | GREEN | BLUE


if __name__ == '__main__':
    print(int(Color.RED))     # 1   ※２進数だと   1
    print(int(Color.GREEN))   # 2   ※２進数だと  10
    print(int(Color.BLUE))    # 4   ※２進数だと 100
    print(int(Color.PURPLE))  # 5   ※２進数だと 101
    print(int(Color.WHITE))   # 7   ※２進数だと 111
```


# まとめ
python3.6になってから `auto` 関数ができてEnumの書き方が楽になった感じがありますね。
また、Flag やIntFlag などの特殊な使い方ができるenumも用意されていて面白いです。で、結局これらのうちどれを使えばいいの？と思うと思いますが、公式ドキュメントにこう書かれてあります。 

> 注釈 ほとんどの新しいコードでは、 Enum と Flag が強く推奨されます。 というのは、 IntEnum と IntFlag は (整数と比較でき、従って推移的に他の無関係な列挙型と比較できてしまうことにより) 列挙型の意味論的な約束に反するからです。 IntEnum と IntFlag は、 Enum や Flag では上手くいかない場合のみに使うべきです; 例えば、整数定数を列挙型で置き換えるときや、他のシステムとの相互運用性を持たせたいときです。

参照元: https://docs.python.org/ja/3.6/library/enum.html

普通はEnum, Flagを使いましょう、どうしても整数を使いたいときはIntEnum, IntFlagを使いましょうとのことです。また Enumでも Flag でも `auto` 関数を使うのが良いでしょう。

快適なenumライフを！