Pythonの==演算子とis演算子の違い
Pythonにおいて、==演算子は2つのオブジェクトの値が等価(同値)かを比較・判定し、is演算子は2つのオブジェクトが同一かを比較・判定する。
==演算子は等価比較
==演算子は2つのオブジェクトの値が等しければTrue、等しくなければFalseを返す。
a = 100
b = 100
c = 200
print(a == b)
# True
print(a == c)
# False
反対に、!=演算子は2つのオブジェクトの値が等しくなければTrue、等しければFalseを返す。
print(a != b)
# False
print(a != c)
# True
注意点: 数値に対する等価比較
整数intや浮動小数点数float、複素数complexなどの数値を表す型の場合、異なる型であってもその値をもとに比較される。
print(100 == 100.0)
# True
print(100 == 100 + 0j)
# True
さらに、真偽値boolは整数intのサブクラスであり、Trueは1、Falseは0とみなされる。
print(1 == True)
# True
print(0.0 == False)
# True
値も型も等しいか判定したい場合はtype()とandを使う。
a = 100
b = 100.0
print(a == b)
# True
print(a == b and type(a) == type(b))
# False
なお、文字列strは内容に関わらず数値と等価とは判定されない。文字列が表す値を数値と比較したい場合はint()やfloat()で文字列を数値に変換する。
print(100 == '100')
# False
print(100 == int('100'))
# True
is演算子はオブジェクトの同一性比較
is演算子は2つのオブジェクトが同一であればTrue、同一でなければFalseを返す。
同じ値(==の結果がTrue)であっても、異なるオブジェクトであればisはFalseを返す。
l1 = [1, 2, 3]
l2 = [1, 2, 3]
print(l1 == l2)
# True
print(l1 is l2)
# False
変数を別の変数に代入するような場合は、2つの変数が同じオブジェクトを指すのでisはTrueを返す。
l3 = l1
print(l1 is l3)
# True
リストや辞書などのイミュータブル(変更可能)なオブジェクトでは、2つの変数が同じオブジェクトを指しているときに一方を変更すると他方も変更される。
l1[0] = 100
print(l1)
# [100, 2, 3]
print(l2)
# [1, 2, 3]
print(l3)
# [100, 2, 3]
is演算子とは反対に、is not演算子は2つのオブジェクトが同一でなければTrue、同一であればFalseを返す。
print(l1 is not l2)
# True
print(l1 is not l3)
# False
同一性はid()関数で判定される
オブジェクトの同一性の判定には、オブジェクトの識別値を返す組み込み関数id()が使われる。
print(id(l1))
# 4388188480
print(id(l2))
# 4388187456
print(id(l3))
# 4388188480
同一オブジェクト(例ではl1とl3)が同じ識別値であることが確認できる。
注意点: イミュータブルな型の同一性比較
整数intや文字列strなどのイミュータブル(変更不可)な型のオブジェクトに対するisやis notでの比較は注意が必要。
新たにオブジェクトを生成したときに、既存のオブジェクトへの参照が返される場合とそうでない場合がある。
例えばintの場合、-5から256の範囲の値を生成すると既存のオブジェクトへの参照が返されるが、その範囲外の値は別のオブジェクトとして生成される。
The current implementation keeps an array of integer objects for all integers between
-5and256. When you create an int in that range you actually just get back a reference to the existing object. 整数型オブジェクト (integer object) — Python 3.11.3 ドキュメント
a = 256
b = 256
print(a is b)
# True
a = 257
b = 257
print(a is b)
# False
文字列strの場合、英数字とアンダースコア_のみの文字列はキャッシュされ、以降はそのオブジェクトへの参照が返されるが、他の文字を含む文字列は別のオブジェクトとして生成される。
a = 'abc_123'
b = 'abc_123'
print(a is b)
# True
a = 'abc_123?'
b = 'abc_123?'
print(a is b)
# False
さらに、例えば、2つの変数への代入を1行で書くと同一オブジェクトになるなど、書き方によって結果が異なる場合がある。
a, b = 257, 257
print(a is b)
# True
a, b = 'abc_123?', 'abc_123?'
print(a is b)
# True
また、これらはCPython 3.11で実行した場合の結果であり、実装やバージョンによって異なる可能性もある。
isの方が==より高速なので、仕様を熟知した上でisを使うケースはあるかもしれないが、基本的にはイミュータブルなオブジェクトに対してはisではなく==で比較すればよい。
==演算子はオーバーロード可能、is演算子は不可
==演算子と!=演算子には、対応する特殊メソッド__eq__と__ne__があり、オーバーロード可能。
class MyClass:
def __eq__(self, other):
return '__eq__'
def __ne__(self, other):
return '__ne__'
my_obj = MyClass()
print(my_obj == 100)
# __eq__
print(my_obj != 100)
# __ne__
上は==, !=演算に対して常に'__eq__', '__ne__'を返すという極端な例だが、ユーザー定義クラスの==, !=演算は独自にカスタマイズされている可能性がある。
一方、is演算子とis not演算子には対応する特殊メソッドがなく、オーバーロードできない。必ずオブジェクトの同一性を比較・判定される。
Noneの判定は"is None"または"is not None"
Noneは値が存在しないことを表す組み込み定数。
- 関連記事: PythonにおけるNoneの判定
Pythonのコーディング規約PEP8において、Noneとの比較は==, !=ではなくis, is notを使うべき、と記載されている。
Noneのようなシングルトンと比較をする場合は、常にisかis notを使うべきです。絶対に等値演算子を使わないでください。 プログラミングに関する推奨事項 — pep8-ja 1.0 ドキュメント
a = None
print(a is None)
# True
print(a is not None)
# False
PEP8には明記されていないが、以下の理由が考えられる。
Noneはシングルトン(NoneType型の唯一のインスタンス)だから- 上述の整数や文字列の例のように
isの結果が値によって変わる懸念がない
- 上述の整数や文字列の例のように
is,is notはオーバーロード不可で安全だから==,!=は演算子オーバーロードによってカスタマイズされている可能性がある
is,is notの方が==,!=より高速だから
2番目の理由の例。__eq__の実装によっては== NoneはNoneでない値に対してもTrueを返す可能性がある。is Noneは常に正しく判定される。
class MyClass:
def __eq__(self, other):
return True
my_obj = MyClass()
print(my_obj == None)
# True
print(my_obj is None)
# False
なお、非数を表すnanはfloat型の値でありNoneとは別物。nanの判定にはmath.isnan()などを使う。
- 関連記事: Pythonにおけるnanの判定