Python: True, False, None の比較
前提: #Python3
定石
None は is, is not で比較する(理由(1)のため)
True / False は 比較演算子(==, !=)に渡さない(理由(2)のため)
True / False は is, is not で比較しない (理由(3)のため)
真偽値を返す関数の返り値を is True 等で比較しない(理由(4)のため)
(余談) Enum値は is, is not で比較する(理由(1)のため)
True, False はシングルトンなのでisで比較可能ですが、このページではisで比較するべきでない理由をまとめています。
上記の定石に沿わないコードは、if文がどっちに分岐するかの可能性が広くなりすぎ、バグの原因になりやすい。
また、正しく理解するには、比較対象オブジェクトが何で、どのようなクラス実装になっているかまで確認しないといけなくなってしまう。
理由
1. is での比較は obj.__eq__() の影響を受けない
2. == での比較は obj.__eq__() の実装で結果をカスタマイズできる
3. 真偽判定は obj.__bool__() の実装でカスタマイズできる
4. Python標準の関数は、真偽値を返すとき、 Trueまたは1を返すことになっている。この戻り値をisで比較すると True is 1 が偽となり期待と異なる結果になる
コード例
None は is, is not で比較する(理由(1)のため)
期待を裏切るクラス(意図せず__eq__の実装を間違えると起こる)
code:python
class DummyNone:
def __eq__(self, other):
return True
obj = DummyNone()
obj is None # => False. Noneとの比較は、obj自体がNoneかどうかを知りたい時に使われる
obj == None # ==> True. Noneとの比較を同値性検査で行うと、クラス実装の影響を受けるため、考えるべきことが増える
True / False は 比較演算子(==, !=)に渡さない(理由(2)のため)
code:python
class DummyBool:
def __eq__(self, other):
return True
obj = DummyBool()
if obj == False: # __eq__ の実装によって判定されるため、objがなんのインスタンスか意識する必要がでてしまう
print('False!')
if obj: # objが何のインスタンスかは関係無く、真偽を知りたい
print('True!')
True / False は is, is not で比較しない (理由(3)のため)
code:python
class DummyBool:
def __bool__(self):
return True
obj = DummyBool()
if obj is True: # DummyBoolクラスのインスタンスはTrueそのものではないため、期待する動作にならない
print('True!')
if obj: # __bool__ の実装によって判定される。objがTrueやFalseそのものであるかどうか知りたいわけではない
print('True!')
なお、 __bool__() がなくて __len__() がある場合、その値が0なら False, それ以外なら True になる
真偽値を返す関数の返り値を is True 等で比較しない(理由(4)のため)
code:python
def some_function():
return 1
if some_function() is True: # => 偽
... # ここは実行されない
if some_function(): # => 真
... # ここは実行される
リファレンス
組み込み型 — Python 3.7.2rc1 ドキュメント
真理値判定
どのようなオブジェクトでも真理値として判定でき、 if や while の条件あるいは以下のブール演算の被演算子として使えます。
オブジェクトは、デフォルトでは真と判定されます。ただしそのクラスが __bool__() メソッドを定義していて、それが False を返す場合、または __len__() メソッドを定義していて、それが 0 を返す場合は偽と判定されます。主な組み込みオブジェクトで偽と判定されるものを次に示します:
偽であると定義されている定数: None と False
数値型におけるゼロ: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
空のシーケンスまたはコレクション: '', (), [], {}, set(), range(0)
ブール値の結果を返す演算および組み込み関数は、特に注釈のない限り常に偽値として 0 または False を返し、真値として 1 または True を返します。 (重要な例外: ブール演算 or および and は常に被演算子のうちの一つを返します。)
3. データモデル — Python 3.7.2rc1 ドキュメント
object.__bool__(self)
真理値テストや組み込み演算 bool() を実装するために呼び出されます; False または True を返さなければなりません。このメソッドが定義されていないとき、 __len__() が定義されていれば呼び出され、その結果が非 0 であれば真とみなされます。クラスが __len__() も __bool__() も定義していないければ、そのクラスのインスタンスはすべて真とみなされます。
歴史的経緯
True / False はPython2.3で登場(PEP-0285)
記事
https://atsuoishimoto.hatenablog.com/entry/2019/09/20/085912
https://www.python-izm.com/tips/difference_eq_is/
https://teratail.com/questions/111421