Pythonの==演算子とis演算子の違い

Modified: | Tags: Python

Pythonにおいて、==演算子は2つのオブジェクトの値が等価(同値)かを比較・判定し、is演算子は2つのオブジェクトが同一かを比較・判定する。

==演算子は等価比較

==演算子は2つのオブジェクトの値が等しければTrue、等しくなければFalseを返す。

a = 100
b = 100
c = 200
print(a == b)
# True

print(a == c)
# False
source: eq_is.py

反対に、!=演算子は2つのオブジェクトの値が等しくなければTrue、等しければFalseを返す。

print(a != b)
# False

print(a != c)
# True
source: eq_is.py

注意点: 数値に対する等価比較

整数intや浮動小数点数float、複素数complexなどの数値を表す型の場合、異なる型であってもその値をもとに比較される。

print(100 == 100.0)
# True

print(100 == 100 + 0j)
# True
source: eq_is.py

さらに、真偽値boolは整数intのサブクラスであり、True1False0とみなされる。

print(1 == True)
# True

print(0.0 == False)
# True
source: eq_is.py

値も型も等しいか判定したい場合はtype()andを使う。

a = 100
b = 100.0
print(a == b)
# True

print(a == b and type(a) == type(b))
# False
source: eq_is.py

なお、文字列strは内容に関わらず数値と等価とは判定されない。文字列が表す値を数値と比較したい場合はint()float()で文字列を数値に変換する。

print(100 == '100')
# False

print(100 == int('100'))
# True
source: eq_is.py

is演算子はオブジェクトの同一性比較

is演算子は2つのオブジェクトが同一であればTrue、同一でなければFalseを返す。

同じ値(==の結果がTrue)であっても、異なるオブジェクトであればisFalseを返す。

l1 = [1, 2, 3]
l2 = [1, 2, 3]
print(l1 == l2)
# True

print(l1 is l2)
# False
source: eq_is.py

変数を別の変数に代入するような場合は、2つの変数が同じオブジェクトを指すのでisTrueを返す。

l3 = l1
print(l1 is l3)
# True
source: eq_is.py

リストや辞書などのイミュータブル(変更可能)なオブジェクトでは、2つの変数が同じオブジェクトを指しているときに一方を変更すると他方も変更される。

l1[0] = 100
print(l1)
# [100, 2, 3]

print(l2)
# [1, 2, 3]

print(l3)
# [100, 2, 3]
source: eq_is.py

is演算子とは反対に、is not演算子は2つのオブジェクトが同一でなければTrue、同一であればFalseを返す。

print(l1 is not l2)
# True

print(l1 is not l3)
# False
source: eq_is.py

同一性はid()関数で判定される

オブジェクトの同一性の判定には、オブジェクトの識別値を返す組み込み関数id()が使われる。

print(id(l1))
# 4388188480

print(id(l2))
# 4388187456

print(id(l3))
# 4388188480
source: eq_is.py

同一オブジェクト(例ではl1l3)が同じ識別値であることが確認できる。

注意点: イミュータブルな型の同一性比較

整数intや文字列strなどのイミュータブル(変更不可)な型のオブジェクトに対するisis notでの比較は注意が必要。

新たにオブジェクトを生成したときに、既存のオブジェクトへの参照が返される場合とそうでない場合がある。

例えばintの場合、-5から256の範囲の値を生成すると既存のオブジェクトへの参照が返されるが、その範囲外の値は別のオブジェクトとして生成される。

The current implementation keeps an array of integer objects for all integers between -5 and 256. 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
source: eq_is.py

文字列strの場合、英数字とアンダースコア_のみの文字列はキャッシュされ、以降はそのオブジェクトへの参照が返されるが、他の文字を含む文字列は別のオブジェクトとして生成される。

a = 'abc_123'
b = 'abc_123'
print(a is b)
# True

a = 'abc_123?'
b = 'abc_123?'
print(a is b)
# False
source: eq_is.py

さらに、例えば、2つの変数への代入を1行で書くと同一オブジェクトになるなど、書き方によって結果が異なる場合がある。

a, b = 257, 257
print(a is b)
# True

a, b = 'abc_123?', 'abc_123?'
print(a is b)
# True
source: eq_is.py

また、これらは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__
source: eq_is.py

上は==, !=演算に対して常に'__eq__', '__ne__'を返すという極端な例だが、ユーザー定義クラスの==, !=演算は独自にカスタマイズされている可能性がある。

一方、is演算子とis not演算子には対応する特殊メソッドがなく、オーバーロードできない。必ずオブジェクトの同一性を比較・判定される。

Noneの判定は"is None"または"is not None"

Noneは値が存在しないことを表す組み込み定数。

Pythonのコーディング規約PEP8において、Noneとの比較は==, !=ではなくis, is notを使うべき、と記載されている。

None のようなシングルトンと比較をする場合は、常に isis not を使うべきです。絶対に等値演算子を使わないでください。 プログラミングに関する推奨事項 — pep8-ja 1.0 ドキュメント

a = None
print(a is None)
# True

print(a is not None)
# False
source: eq_is.py

PEP8には明記されていないが、以下の理由が考えられる。

  1. Noneはシングルトン(NoneType型の唯一のインスタンス)だから
    • 上述の整数や文字列の例のようにisの結果が値によって変わる懸念がない
  2. is, is notはオーバーロード不可で安全だから
    • ==, !=は演算子オーバーロードによってカスタマイズされている可能性がある
  3. is, is notの方が==, !=より高速だから

2番目の理由の例。__eq__の実装によっては== NoneNoneでない値に対しても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
source: eq_is.py

なお、非数を表すnanfloat型の値でありNoneとは別物。nanの判定にはmath.isnan()などを使う。

関連カテゴリー

関連記事