Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
194
Help us understand the problem. What is going on with this article?
@macinjoke

enum 超まとめ python3.8

追記1

本記事は当初python3.6 のenumについて書きました。
python3.7 からの機能は以下の通りです、大して変わってないです。
https://qiita.com/ksato9700/items/35a0bdc04693b3b09757#enum

追記2

python3.8 が出ましたが、enumについては変更ないです。 公式の3.8差分

前置き

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

目次

  • 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 では上手くいかない場合のみに使うべきです; 例えば、整数定数を列挙型で置き換えるときや、他のシステムとの相互運用性を持たせたいときです。

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

快適なenumライフを!

194
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
macinjoke
Webをやっていく 主戦力: React, TypeScript
dwango
Born in the net, Connected by the net.

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
194
Help us understand the problem. What is going on with this article?