2012年1月5日木曜日

ブール演算について

Pythonのブール演算について、メモ書きします。

Python2.5 から三項演算子が使えるようになり、私も三項演算子を常用している。Pythonの三項演算子はC言語などとは式の順序が違うため、ちょっと戸惑うが、慣れれば便利に使える。

三項演算子が登場する以前は、ブール演算を三項演算子の代用にしていた。このためフレームワークなどのソースを見ると、ブール演算がよく使われている。

ブール演算が使われているソースを見ると、「あれ、どうだったかな」とその場で調べて納得するのだが、個人的には使わないタイプのコードのためすぐ忘れてしまう。そんな事が何度もあるので、今回、ブール演算をまとめた記事をメモ代りに書いてみます。

真理値テスト

if や while文の条件式に、オブジェクトを指定できる。この時指定するオブジェクトは次のルールで、ブール演算(論理演算)される。

偽(False)として扱われるもの
  • None
  • False
  • 数値型におけるゼロ。例えば 0, 0L, 0.0, 0j 。
  • 空のシーケンス型。例えば '', (), [] 。
  • 空のマッピング型。例えば {} 。
  • __nonzero__() または __len__() メソッドが定義されているようなユーザ定義クラスのインスタンスで、それらのメソッドが整数値ゼロまたは bool 値の False を返すとき。

それ以外の値は全て真であると見なされます — 従って、ほとんどの型のオブジェクトは常に真です。

参考: Python 2.7ja1 documentation - 5.1. 真理値テスト

オブジェクトのブール値を確認するには、組み込み関数 bool() を使うと良い。

>>> value = 'abcd'
>>> bool(value)
True
>>> value = ''
>>> bool(value)
False
ブール演算子

ブール演算子としては、or , and , not が存在する。各演算子の働きと優先順位は次のようになる。

以下にブール演算子を示します。優先度の低いものから順に並んでいます。

演算結果注釈
x or yx が偽なら y, そうでなければ x(1)
x and yx が偽なら x, そうでなければ y(2)
not xx が偽なら True, そうでなければ False(3)

注釈
(1)これは、短絡的な演算子であり、一つめの引数が False のときにのみ、二つめの引数を評価します。
(2)これは、短絡的な演算子であり、一つめの引数が True のときにのみ、二つめの引数を評価します。
(3)not は非ブール演算子よりも低い演算優先度なので、 not a == b は not (a == b) と評価され、a == not b は構文エラーとなります。

参考: Python 2.7ja1 documentation - 5.2. ブール演算 — and, or, not

and と or を試してみる。

>>> 'abcd' and 'efgh'
'efgh'
>>> '' and 'efgh'
''
>>> 'abcd' or 'efgh'
'abcd'
>>> '' or 'efgh'
'efgh'

and と or では全く逆の動きをするのがわかる。

ブール演算子を使った三項式

and と or のブール演算子の性格を利用して、三項式を作ることができる。次のように組み合わせる。

条件 and 真の場合の値 or 偽の場合の値

試してみる。

>>> a = 'abcd'
>>> b = 'efgh'
>>> True and a or b
'abcd'
>>> False and a or b
'efgh'

三項演算子のように動いた。しかしこの式には重大な欠陥がある。もし「真の場合の値」が偽(False)だったら、どうなるか?。

>>> a = ''
>>> b = 'efgh'
>>> True and a or b
'efgh'
>>> False and a or b
'efgh'

条件に関わりなく、同じ結果になってしまう。or演算子は False or b の場合、変数 b を返すため、このような結果になる。この問題を回避するためには、「真の場合の値」をリストもしくはタプル値にすればよい。

>>> a = ''
>>> b = 'efgh'
>>> True and [a] or b
['']
>>> False and [a] or b
'efgh'
>>> True and (a,) or b
('',)
>>> False and (a,) or b
'efgh'

変数 a は False でもリストもしくはタプルは空ではないため、Falseにはならない。ただ、変数 a を返す時はリスト・タプル値が返ってきてしまうため、次のように変更する。

>>> a = ''
>>> b = 'efgh'
>>> (True and [a] or [b])[0]
''
>>> (False and [a] or [b])[0]
'efgh'
>>> (True and (a,) or (b,))[0]
''
>>> (False and (a,) or (b,))[0]
'efgh'

これで上手く動くようだ。

まとめると、ブール演算子を利用した三項式は次のようになる。

■ 「真の場合の値」が偽(False)になる可能性がない場合

条件 and 真の場合の値 or 偽の場合の値

■ 「真の場合の値」が偽(False)になる可能性がある場合

(条件 and [真の場合の値] or [偽の場合の値])[0]
もしくは
(条件 and (真の場合の値,) or (偽の場合の値,))[0]

ちなみに上の例を、(ブール演算を利用しない)三項演算子で書くと次のようになる。

>>> a = ''
>>> b = 'efgh'
>>> a if True else b
''
>>> a if False else b
'efgh'

三項演算子を使用した方が、なにかと簡単で安全ですね。

参考
Dive into Python 5.4. - 4.6.1.and−or トリックの使い方
紫藤のページ - 2.1. 三項演算子