1つの整数で複数のフラグを扱う方法!!

takkun
どうも!たっくんです。
忙しいことを理由に、家が荒れてきました、、、orz

ツールを作っていると、複数のフラグを扱う事よくありますよね!例えばこんな感じ。

flagA = False
flagB = True
flagC = False

または、配列を使って1つにまとめる場合もあると思います!

flags = [False, True, False]

でも、コレ「2進数の性質」を使うと1つの数値で管理することができるんです!!(`・ω・´)ゞ

2進数って?

普段使っている数値は、「0~9」の10種類の数字を使って数値を扱っていますね!2進数になると、「0と1」の2個の数字を使って数値を扱います!

と入ってもよくわからないと思うので、表にしています!(`・ω・´)ゞ

10進数 2進数
0 0
1 1
2 10
3 11
4 100
5 101
6 110
7 111
8 1000
9 1001
10 1010

2進数は、10進数の「0」「2」「4」「8」「16」の時に桁が増えていきます!頻度が多いですね(;´∀`)

あれ?

2,4,8、16ってどこかで
見たことある並び。。。

そう!CGでもよく出てくる、「2の乗数」で桁が増えていきます!試しに、128までやってみると以下のようになります(*´ω`*)b

10進数 2進数
0 (2の0乗) 0
2 (2の1乗) 10
4 (2の2乗) 100
8 (2の3乗) 1000
16 (2の4乗) 10000
32 (2の5乗) 100000
64 (2の6乗) 1000000
128 (2の7乗) 10000000

あえて右寄せにしましたが、2進数が1桁1桁スイッチのように見えませんか!?例えば、10進数の「8」の場合、、、

更に面白いのが足し算、引き算です(゚∀゚)

10進数の「8+2=10」ですが、2進数の場合は「1000+10=1010」になります。これをまたスイッチの画像にすると、、、

2の乗数」だけで数値を作ると、1つの数値に「たっくさーーーーーーーーーんのフラグ」を埋め込めることができるんです!!(*´ω`*)

他にも、データ量が少なくて済んだり、計算が早かったりします。

でも、一番の目的は、配列を使うことなく「一つのデータに複数の情報を詰め込める」ことにあると思います(`・ω・´)ゞ

Pythonでの実装!

手順として重要なのは3つの要素です。

  1. フラグを定義
  2. フラグの建て方
  3. フラグの検出

この3つの要素を順番に見ていきたいと思います!(`・ω・´)ゞ

フラグを定義

フラグの定義は、Pythonの場合は「モジュールのグローバル変数」か、「クラス変数」にするといいと思います!

モジュールのグローバル変数の場合

# モジュールのグローバル変数の場合
# 10進数を使って定義する場合
ITEM_FLAG_A = 1
ITEM_FLAG_B = 2
ITEM_FLAG_C = 4
ITEM_FLAG_D = 8

# 2進数を使って定義する場合(頭に「0b」をつけて定義します)
ITEM_FLAG_A = 0b1
ITEM_FLAG_B = 0b10
ITEM_FLAG_C = 0b100
ITEM_FLAG_D = 0b1000

# クラス変数の場合
class Item(object):
    ITEM_FLAG_A = 1
    ITEM_FLAG_B = 2
    ITEM_FLAG_C = 4
    ITEM_FLAG_D = 8

フラグの建て方

次は、変数の数値にフラグをONにしたり、OFFにしたりする方法です。Pythonでは「ビット演算」を使って行います(`・ω・´)ゞ

直接数値を使わず、フラグの定義をした変数を使って計算することをオススメします!あとで、何のフラグを編集したかすごく見やすいです(*´ω`*)b

# すべてのフラグOFFの変数を定義
flags = 0

# AとBのフラグをONにした変数を定義
flags = ITEM_FLAG_A | ITEM_FLAG_B
print flags

# CのフラグをONにする
flags |= ITEM_FLAG_C
print flags

# BのフラグをOFF
flags -= flags & ITEM_FLAG_B
print flags 

フラグをONにする時は「|」、フラグをOFFにする場合は「&」を使用します。フラグをOFFにしたい場合だけ、ちょっとややっこしい感じですね(;´∀`)

足し算、引き算をしてしまうとフラグがバグってしまうことがあるのでご注意ください!

#ビット演算の場合
flags = 0

flags |= ITEM_FLAG_A
print flags
#1

flags |= ITEM_FLAG_A
print flags
# 1 何度やっても問題ない。事前にフラグがONか調べる必要もなし!

# 足し算の場合
flags = 0

flags += ITEM_FLAG_A
print flags
# 1

flags += ITEM_FLAG_A
print flags
# 2 フラグがズレてしまう。

フラグの検出

フラグの検出は、if分を使って行きます。「&」を使い、2進数から必要な部分だけを取り出しフラグが立っているか確認する感じです!

flags = ITEM_FLAG_A | ITEM_FLAG_C

# AのフラグがONか確認
if flags & ITEM_FLAG_A:
    print 'ITEM_FLAG_A is True'
# ITEM_FLAG_A is True

# BのフラグがOFFか確認
if not flags & ITEM_FLAG_B:
    print 'ITEM_FLAG_A is False'
# ITEM_FLAG_A is False