追記
本記事は当初python3.6 のenumについて書きました。
- python3.7 からの機能は以下の通りです、大して変わってないです。
https://qiita.com/ksato9700/items/35a0bdc04693b3b09757#enum- 3.8, 3.9, 3.10 が出ましたが、enumについては変更ないです。
- 3.11 は色々更新がありました。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クラス。説明するよりコードを見たほうが早い。
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型と比較することができる。 違いはそれだけ。
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 のどれにも適用可能。
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
関数を使うことが推奨されている。
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型と比較することができる。
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 ※2進数だと 1
print(int(Color.GREEN)) # 2 ※2進数だと 10
print(int(Color.BLUE)) # 4 ※2進数だと 100
print(int(Color.PURPLE)) # 5 ※2進数だと 101
print(int(Color.WHITE)) # 7 ※2進数だと 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ライフを!