この記事はKCS Advent Calendar 2023の8日目の記事です。
←7日目 9日目→
pythonのis
と==
の違い
pythonには"同じであること"を判断するような比較演算子が二つ存在します。一つは==
、もう一つはis
です。
==
は各クラスに設定されている、__eq__
メソッドの結果を返します。基本的には値が等価であるかどうかという意味に使われ、そのようなメソッドが設定されています。
一方で、is
はオブジェクトのid
を取得し、それが一致しているかを判定します。つまり、オブジェクトが同一であるかどうかを判定します。この挙動は変更ができません。
pythonのすべてのオブジェクトは"識別子"であるidを持っています。組み込み関数のid()
を使うことで確認することができます。
==
がTrueなのに、is
がFalseである例
これはめっちゃあります。
例えば、以下のものがあげられます。
a = [1,2,3]
b = [1,2,3]
c = 100000
d = sum(1 for i in range(100000))
e = "hello"
f = "".join(["h","e","l","l","o"])
print(a == b,a is b)
print(c == d,c is d)
print(e == f,e is f)
上記のコードのprintの出力はすべてTrue False
となります。(CPython3.11.4で検証)
==
だとしても、オブジェクトが異なる場合は当然is
はFalseとなります。
なお、CPython
では上記の結果となりますが、別のpython処理系では異なる結果が返ることがあります。(例えばPyPy
では、c is d
はTrueになります。)
is
がTrueなのに、==
がFalseである例
そもそも、pythonのドキュメントにこのような記述があります。
等価比較は反射的でなければなりません。 つまり、同一のオブジェクトは等しくなければなりません:
x is y ならば x == y
同じオブジェクトであるのに==
がFalseを返すというのは、結構変な話ですよね。
当然、以下のようにクラスを書いてあげれば、これが実現できます。
class hoge():
def __eq__(self,other):
return False
a = hoge()
print(a == a,a is a)
結果はFalse True
となりました。実際にこのような挙動をするクラスはあるのでしょうか?
import decimal
a = float('NaN')
b = decimal.Decimal('NaN')
print(a == a,a is a)
print(b == b,b is b)
上のコードを実行してみると、結果がFalse True
になります。
Pythonのドキュメントを読んでみると、以下のような記述があります。
非数値である float('NaN') と decimal.Decimal('NaN') は特別です。 数と非数値との任意の順序比較は偽です。 直観に反する帰結として、非数値は自分自身と等価ではないことになります。 例えば x = float('NaN') ならば、 3 < x, x < 3, x == x は全て偽で、x != x は真です。 この振る舞いは IEEE 754 に従ったものです。
よって、x == xが偽という珍しいことが起こったのですね。
おまけ
こんなリストを用意してみます。
a = [float('NaN')]
当然、a is a
はTrueですよね。では、a == a
はどうなるでしょうか。
print(a == a) # True
予想した結果と一致しましたか?