Back to page

− Links

 Print 

Python​/FlagとIntFlag :: NJF Wiki

xpwiki:Python/FlagとIntFlag

Flag anchor.png[1] Edit [2]

注意:FlagやIntFlagEdit[3]は列挙型Enumの一種です。Enumを知らない場合はPython​/列挙型[4]を先に読むのを推奨します。

昔からあるプログラムの書き方で、ビット毎にフラグを設定して一つの変数に複数のフラグを保存する方法があります。

ビット単位で管理するのでメモリが節約できます。また一般にビット演算は高速なのでプログラムの高速化も狙えます。

これと同じようなことを実現する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と演算したいときには次のIntFlagEdit[3]型を使います。

Page Top

IntFlagEdit[3] anchor.png[5] Edit [6]

IntFlagEdit[3]型は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'

しかし、IntFlagEdit[3]型なら問題なく実行されます。

IntFlagEdit[3]型での結果:

PlayerStatus.POISON|SLEEP

直接数値が使えないのでFlag型の方がマジックナンバーが発生しにくいという利点があります。

一方でIntFlagEdit[3]型は計算結果や外部入力の数値をそのままフラグとして演算できるという利点があります。

基本的にFlag型を使い、フラグを整数と演算する必要がある場合はIntFlagEdit[3]型を使うというのが良いかもしれません。

Page Top

まとめ anchor.png[7] Edit [8]

Flagはビット演算でのフラグの管理を使いやすくするクラスです。 IntFlagEdit[3]はそのFlagを整数としても扱えるようにした物です。

フラグがたくさんあって整理が難しい場合や、少しでも処理を高速化したい場合には便利なクラスです。

ただし、現在のようにメモリも大容量でCPUも速くなった時代にフラグ処理が少し軽くなる程度の工夫が必要かどうかはちょっと微妙な気もします。特にPythonのようなスクリプト言語は他の部分が遅いのでフラグがボトルネックにはなりにくく、ビットフラグをよく使うのはCなどのコンパイル言語というイメージです。そういった言語で使い慣れている人にはFlagやIntFlagEdit[3]は使いやすいでしょう。

フラグが少なく処理が簡単な場合は真偽型boolをそのまま使うか、いくつかまとめたクラスを作って使う方が楽です。


Last-modified: 2019-05-26 (Sun) 22:34:44 (JST) (1177d) by njf