Ad

基本 anchor.png Edit

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型を使わずに手で直接値を指定してしまうという、いわゆるマジックナンバーの問題をある程度予防してくれるというメリットがあります。

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

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

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

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を使うと定数をまとめてあつかいやすくなります。

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

Page Top

auto anchor.png Edit

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
Page Top

派生列挙型 anchor.png Edit

Page Top

IntEnumEdit anchor.png Edit

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

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

Page Top

IntFlagEditとFlag anchor.png Edit

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

Page Top

まとめ anchor.png Edit

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

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

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


Front page   Edit Freeze Diff Backup Upload Copy Rename ReloadPrint View   New Page Page list Search Recent changes   Help   RSS of recent changes (RSS 1.0) RSS of recent changes (RSS 2.0) RSS of recent changes (RSS Atom) Powered by xpWiki
Counter: 29, today: 2, yesterday: 0
Princeps date: 2019-05-26 (Sun) 01:56:40
Last-modified: 2019-05-26 (Sun) 22:02:18 (JST) (22d) by njf
広告

ログイン

ユーザー名:


パスワード:





パスワード紛失


NJF