ページへ戻る

− Links

 印刷 

Python​/列挙型 のソース :: NJF Wiki

xpwiki:Python/列挙型のソース

« Prev[3]  
*基本 [#oda99e39]

Pythonの列挙型enumは何かひとまとまりの定数を定義するときに便利な型で、Python3.4から導入されました。

例えば、ゲームのプログラムで今どの画面かを識別するために以下のような変数を定義したとします。

 SCREEN_TITLE = 1 #タイトル画面
 SCREEN_PLAYING = 2 #プレイ中画面
 SCREEN_GAME_OVER = 3 #ゲームオーバー画面
 SCREEN_TRANSITION = 4 #画面遷移中

このままでもプログラムできますが、もう少しまとめてあつかいたい、と考える人もいるでしょう。
また、ただの変数なのでうっかり書きかえないかも心配です。

これをenumを使えば、以下のように書き直せます。

 from enum import Enum
 
 class Screen(Enum):
     TITLE = 1
     PLAYING = 2
     GAME_OVER = 3
     TRANSITION = 4

使う時は

 Screen.PLAYING

などとすれば参照できます。

比較するときはメンバー同士で行う必要があります。

 print(Screen.TITLE == 1)
 print(Screen.TITLE == Screen.TITLE)

結果:

 False
 True

Enumは他の型の値と直接比較できないので注意してください。
めんどうなようですが、こうなっているとせっかく定義したEnum型を使わずに手で直接値を指定してしまうという、いわゆるマジックナンバーの問題をある程度予防してくれるというメリットがあります。

必要であれば、後述のIntEum型なら整数との比較が可能です。

この記事では数値で定義しましたが、文字列で定義することも可能です。

また、これらは実質定数として扱えます。
例えば、

 Screen.GAME_OVER = 2

とすると

 Traceback (most recent call last):
   File "enumtest.py", line 10, in <module>
     Screen.GAME_OVER = 2
   File "/usr/lib/python3.6/enum.py", line 363, in __setattr__
     raise AttributeError('Cannot reassign members.')
 AttributeError: Cannot reassign members.

というような実行時エラーが出ます。

また、
 screen = Screen.PLAYING
 print(screen)

とすると、
 Screen.PLAYING
とクラスとメンバー名が出力されるのでデバッグの時にわかりやすくて便利です。

nameとvalueというプロパティを使えばメンバー名と値も参照できます。

 print(Screen.GAME_OVER.name,Screen.GAME_OVER.value)

結果:
 GAME_OVER 4

ループ処理もできます。
このとき、順番は定義順であることが保証されています。

 for s in Screen:
    print(s)

結果:

 Screen.TITLE
 Screen.PLAYING
 Screen.GAME_OVER
 Screen.TRANSITION

となります。

このようにenumを使うと定数をまとめてあつかいやすくなります。

さらに以下のような便利な機能があります。

*auto [#kfebf26b]

Python3.6から自動で番号をつけてくれるautoという関数が加わりました。

 from enum import Enum, auto
 
 class Screen(Enum):
     TITLE = auto()
     PLAYING = auto()
     GAME_OVER = auto()
     TRANSITION = auto()

とすれば自動で番号をつけてくれます。

例えば上の例でタイトル画面の前にというロード画面を付け加えたい、順番的にできればそれを表す定数は1にしたい、となったときに、手で番号をつけると今までの定義を全て手で1ずらさねばなりません。

 class Screen(Enum):
     LOADING = 1
     TITLE = 2 #ここから下全て1ずらした
     PLAYING = 3
     GAME_OVER = 4
     TRANSITION = 5

enumのメンバー数が多いと編集が大変になります。
autoを使っておけば自動で番号をつけてくれるので「LOADING = auto()」という一行を付け加えるだけでよく、手間が省けます。

 class Screen(Enum):
    LOADING = auto() #この行だけ増やした
    TITLE = auto()
    PLAYING = auto()
    GAME_OVER = auto()
    TRANSITION = auto()

また、enum型は値が一意になる事は保証していません。
次のようなコードは問題なく実行されます。

 class Test(Enum):
 	TEST1 = 1
 	TEST2 = 1
 	TEST3 = 1

しかし、実際には一意な値をつけたいときが多いでしょう。
手で定義するとどうしても書き間違いの可能性が出てきます。
その時は「@unique」というデコレータを使えば一意であることが保証されます。

 @unique
 class Test(Enum):
 	TEST1 = 1
 	TEST2 = 1
 	TEST3 = 1

とすると、実行時エラーになります。

しかし、autoを使っておけば、自動的に値は一意になります。
とりあえず値はなんでもいいという場合にはautoを使うのがおすすめです。

autoを使いつつ、特定の値を同じにしたければ以下のようにします。

 class Test(Enum):
    TEST1 = auto()
    TEST2 = auto()
    TEST3 = auto()
    TEST2_ALT = TEST2
 
 print(Test.TEST2_ALT,Test.TEST2_ALT.value)

結果:
 Test.TEST2 2


*派生列挙型 [#ocba7d30]

**IntEnum [#t17d8986]

Enumとほぼ同じですが、整数や他のIntEnum型との直接の比較が可能になっています。

ほとんどの場合Enumで十分ですが、外部から入力された値に名前をつけたい、他のプログラムとデータのやり取りをするときに独自の名前をつけたい、計算結果に名前をつけたい、といった場合に、その値が整数なら簡単に比較できるようになるので便利です。


**IntFlagとFlag [#k4589686]

Enumにビット演算子を適用可能にしてフラグとして使いやすくした物です。
ここに書くと長くなるのでページを分けて[[Python/FlagとIntFlag]]で説明しています。

*まとめ [#a8d5981a]

列挙型は使わないでもプログラムは書けますし、実際Python3.4までは実装されていませんでしたが、そのころもさほど問題ではありませんでした。

しかし、覚えておくといろいろ便利に使えます。

特に状態遷移などがあり多くの定数が必要なプログラムを書く場合や、Javaなどで列挙型に慣れている人などにはおすすめです。

« Prev[3]