PythonのTrue/False

西尾さんという方が執筆した『コーディングを支える技術』という本に
各言語でどのような値がTrue / Falseとして扱われるのか、という記述があって、
面白かったのでプライベートでよく使っているPythonについて調べてみた。

ちなみにAという変数や式があって、それがbool値では何に該当するのか調べるには、
単に

bool(A)

と書けばOK。


まず数字から。基本的に『ゼロ』はFalseであるらしい。
逆にそれ以外の数字はTrueとなる。

>>> bool(0), bool(0.0), bool(0j), bool(-0)
(False, False, False, False)

>>> bool(1), bool(0.1), bool(1+2j), bool(-1)
(True, True, True, True)

次に文字列。空文字はFalseで、それ以外はTrueになるらしい。

>>> bool(""), bool('')
(False, False)

>>> bool("A"), bool('X'), bool("0"), bool("hogehoge")
(True, True, True, True)

bool型をそのまま評価しても、やはりそのままの意味になる。
ちなみに他言語におけるnullやnilに相当するNoneはFalse扱い。

>>> bool(True), bool(False)
(True, False)

>>> bool(None)
False

リストや辞書などのコンテナは、空であればFalse、何か入っていたらTrue。

>>> bool(()), bool([]), bool({})
(False, False, False)

>>> bool((1, 2, 3)), bool(["ABC"]), bool({"key": "value"})
(True, True, True)

じゃあ入れ子になってたらどうなるの?
試してみると・・・

# リスト
>>> bool([[]])
True

>>> bool([()])
True

>>> bool([{}])
True

# タプル
>>> bool(([],))
True

>>> bool(((),))
True

>>> bool(({},))
True

たとえ空のコンテナであろうと、要素には違いないわけだから、
この場合は「空ではない」ということで、Trueになるらしい。
これはこれで正しい。

ちなみに辞書の中に空の辞書を入れることは不可能。
keyにはHashとして扱えるモノを指定する必要があるため。
{{}: {}}という書き方は出来ないのだ。
タプルなら出来るが、やはり中身があることになるため、結果はTrueになる。

ちなみにタプルは要素が1つだけの場合でもカンマを付ける必要があり、
付けなかった場合は、それ単体として扱われる。
どういうことかというと・・・

>>> bool((()))
False
>>> (())
()  # (())は()であるものとして扱われる

この場合、ただの()として扱われてるわけだから、結果がFalseになるのは正しい。


じゃあ次は・・・自分で作ったクラスや関数オブジェクトだったら?

class MyClass:
	def __init__(self):
		pass

def my_func():
	pass

if __name__ == '__main__':
	c  = MyClass()
	f  = my_func
	
	print(bool(c))  # True
	print(bool(f))  # True

これはどちらもTrueだった。

どうしても自分のクラスが評価された時はFalseを返したい?
ならばこうする。

class MyClass:
	def __bool__(self):
		return False

if __name__ == '__main__':
	c  = MyClass()
	print(bool(c))  # False

__bool__はboolとして評価された時の挙動を定義する。

returnする対象を動的に変えることだって出来るわけだから、
例えば自作コンテナを作る時に役立つかもしれない。
ただ、空っぽの時にFalseやNoneを返すようにするより、
空かどうか調べる手段を用意しておいた方が良さげ。
つまり、is_empty的なメソッドのこと。

ちなみに文字列や数値もあらかじめ定義されたオブジェクトなのだから、
bool()に突っ込んだ時は、__bool__が実行されているものと思われる。

他にも__int__や__str__といった特殊なメソッドを定義することが可能で、
いずれもint()やstr()を実行したときの挙動を設定することができる。

class MyClass:
	def __bool__(self):
		return False

	def __int__(self):
		return 65536

	def __str__(self):
		return "ばかやろー"

if __name__ == '__main__':
	c = MyClass()

	print(bool(c))  # False
	print(int(c))   # 65536
	print(str(c))   # "ばかやろー"

なるほど、勉強になったねー。

他の言語でも調べてみると面白いかも。