Flag
注意:FlagやIntFlagは列挙型Enumの一種です。Enumを知らない場合はPython/列挙型を先に読むのを推奨します。
昔からあるプログラムの書き方で、ビット毎にフラグを設定して一つの変数に複数のフラグを保存する方法があります。
ビット単位で管理するのでメモリが節約できます。また一般にビット演算は高速なのでプログラムの高速化も狙えます。
これと同じようなことを実現するPythonのクラスがFlagです。
例えば、ゲームで状態異常を定義したいとして、次のようなクラスを定義します。
from enum import Flag class PlayerStatus(Flag): SLEEP = 1 #睡眠 POISON = 2 #毒 PARALYSIS = 4 #麻痺
ここで1,2,4は二進数で書けば1,10,100の事で、それぞれ1ビット目、2ビット目,3ビット目をフラグとして使っています。
このように手で書くこともできますが、Python3.6で追加されたautoという関数を使うと自動で二のべき乗を割り振ってくれます。
from enum import Flag ,auto class PlayerStatus(Flag): SLEEP = auto() POISON = auto() PARALYSIS = auto() for s in PlayerStatus: print(s.name,s.value)
結果:
SLEEP 1 POISON 2 PARALYSIS 4
これの良いところは一つの変数に複数のフラグを設定できることです。 例えば、睡眠かつ毒状態のステータスは
status = PlayerStatus.SLEEP | PlayerStatus.POISON
と書きます。これを表示させると、
print(status)
結果:
PlayerStatus.POISON|SLEEP
というふうに、実際にフラグが複数設定されています。 また、Flag型はフラグの状態をわかりやすく出力してくれるのでデバッグの時便利です。
フラグが立っているかどうかは「&」でチェックします。
status = PlayerStatus.SLEEP | PlayerStatus.POISON if status & PlayerStatus.SLEEP: print("SLEEP!") if status & PlayerStatus.POISON: print("POISON!") if status & PlayerStatus.PARALYSIS: print("PARALYSIS!")
結果:
SLEEP! POISON!
フラグをオフにするには以下のように「&」と「~」(否定)を組み合わせます。
status = PlayerStatus.SLEEP | PlayerStatus.POISON status = PlayerStatus.SLEEP & (~PlayerStatus.POISON) if status & PlayerStatus.SLEEP: print("SLEEP!") if status & PlayerStatus.POISON: print("POISON!") if status & PlayerStatus.PARALYSIS: print("PARALYSIS!")
結果:
POISON!
また、Flag型はFlag型同士でなければ演算できないことに注意してください。 Intと演算したいときには次のIntFlag型を使います。
IntFlag
IntFlag型はFlag型とほぼ同じ機能がありますが、Flag型とは異なり整数としても扱えます。
つまり、Flag型では次の処理はエラーとなります。
status = PlayerStatus.SLEEP | 2 print(status)
Flag型での結果:
Traceback (most recent call last): File "enumflgs.py", line 50, in <module> status = PlayerStatus.SLEEP | 2 TypeError: unsupported operand type(s) for |: 'PlayerStatus' and 'int'
PlayerStatus.POISON|SLEEP
直接数値が使えないのでFlag型の方がマジックナンバーが発生しにくいという利点があります。
まとめ
Flagはビット演算でのフラグの管理を使いやすくするクラスです。 IntFlagはそのFlagを整数としても扱えるようにした物です。
フラグがたくさんあって整理が難しい場合や、少しでも処理を高速化したい場合には便利なクラスです。
ただし、現在のようにメモリも大容量でCPUも速くなった時代にフラグ処理が少し軽くなる程度の工夫が必要かどうかはちょっと微妙な気もします。特にPythonのようなスクリプト言語は他の部分が遅いのでフラグがボトルネックにはなりにくく、ビットフラグをよく使うのはCなどのコンパイル言語というイメージです。そういった言語で使い慣れている人にはFlagやIntFlagは使いやすいでしょう。
フラグが少なく処理が簡単な場合は真偽型boolをそのまま使うか、いくつかまとめたクラスを作って使う方が楽です。
Page Info | |
---|---|
Page Name : | Python/FlagとIntFlag |
Page aliases : | None |
Page owner : | njf |
Can Read | |
Groups : | All visitors |
Users : | All visitors |
Can Edit | |
Groups : | All visitors |
Users : | All visitors |