Pythonの「==」と「is」の違いとは?|値とオブジェクトの同一性を理解する

ソフトウェア

はじめに

Pythonでは、2つの値を比較するときに == を使います。しかし、似たような比較に is という演算子もあります。この2つは何が違うのでしょうか。

たとえば、次のコードを見てください。

x = [1, 2, 3]
y = [1, 2, 3]

print(x == y)
print(x is y)

実行結果は、

True
False

となります。

どちらも [1, 2, 3] という同じ内容のリストなのに、== では True、is では False になりました。これは、== と is が比較しているものが違うからです。

== は値が等しいかを比較し、is は同じオブジェクトかどうかを比較します。

この記事では、これまで学んできた変数・オブジェクト・参照の考え方を使って、== と is の違いを順番に整理していきます。

この記事でわかること

この記事では、次の内容の理解を目指します。

  • == と is は何を比較しているのか
  • 同じ値なのに is が False になる理由
  • 代入とコピーで比較結果が変わる理由
  • id() と is の関係
  • 整数や文字列を is で比較してはいけない理由
  • None の比較に is を使う理由

1. 「値が等しい」と「同じオブジェクト」は違う

まず、今回の記事でもっとも重要な考え方を確認しましょう。

Pythonでは、変数名はオブジェクトを参照しています。そのため、2つの変数が同じ内容を持っていても、参照しているオブジェクトが同じとは限りません。

たとえば、

x = [1, 2, 3]
y = [1, 2, 3]

とすると、同じ内容を持つ2つのリストオブジェクトが作られます。x と y が参照するリストは、どちらも [1, 2, 3] ですが、別のオブジェクトです。

ここで、

print(x == y)
print(x is y)

を実行すると、

True
False

となります。

== はリストの内容が等しいかを比較するため True になります。一方、is は同じオブジェクトかどうかを比較するため False になります。

== と is の違い

上の図では、x と y がそれぞれ別のリストオブジェクトを参照しています。2つのリストの内容は同じですが、参照先は別です。

値が等しいことと、同じオブジェクトであることは別です。
== は値の等しさ、is はオブジェクトの同一性を調べます。

▶ Pythonの変数・オブジェクト・参照について詳しく知りたい
Pythonの変数は「箱」ではない?|オブジェクトと参照を理解する
Pythonの変数は本当に「値を入れる箱」なのでしょうか。オブジェクトと参照の仕組みを図解しながら、代入や再代入、ミュータブルとイミュータブル、コピー、参照カウントまでわかりやすく解説します。

2. == は値が等しいかを比較する

まず、== について詳しく見ていきましょう。

== は、2つのオブジェクトが等しいと判断されるかどうかを比較する演算子です。数値や文字列、リストなど、さまざまなオブジェクトの比較に使えます。

たとえば、数値の場合は、

print(10 == 10)
print(10 == 20)

結果は、

True
False

となります。

文字列でも同様です。

print("Python" == "Python")
print("Python" == "Java")

結果は、

True
False

となります。

リストの場合は、対応する要素を比較します。

x = [1, 2, 3]
y = [1, 2, 3]
z = [1, 2, 4]

print(x == y)
print(x == z)

結果は、

True
False

となります。

x と y は別のリストオブジェクトですが、内容が等しいため True になります。

ただし、== が何をもって「等しい」と判断するかは、オブジェクトの型によって異なります。Pythonでは、クラスが等価性の比較方法を定義することもできます。

== はオブジェクトの等価性を比較します。
具体的に何を等しいと判断するかは、型ごとの比較の仕組みによって決まります。

3. is は同じオブジェクトかを比較する

一方、is は値の内容ではなく、2つの変数名が同じオブジェクトを参照しているかどうかを調べます。

たとえば、

x = [1, 2, 3]
y = x

print(x == y)
print(x is y)

結果は、

True
True

となります。

y = x はコピーではなく、y も x と同じリストオブジェクトを参照する代入だからです。そのため、== だけでなく is も True になります。

is は2つの参照先が同じオブジェクトである場合に True になります。
オブジェクトの内容が等しいかどうかは、is の判定基準ではありません。

4. 代入とコピーでは is の結果が変わる

前回の記事では、代入とコピーの違いを学びました。その違いは、is を使うと確認できます。

まず、通常の代入の場合です。

x = [1, 2, 3]
y = x

print(x == y)
print(x is y)

結果は、

True
True

となります。

次に、浅いコピーの場合です。

x = [1, 2, 3]
y = x.copy()

print(x == y)
print(x is y)

結果は、

True
False

となります。

どちらの場合もリストの内容は同じなので、== は True です。しかし、コピーすると新しいリストオブジェクトが作られるため、is は False になります。

代入とコピーで is の結果が変わる理由

上の図では、代入の場合は x と y が同じリストを参照し、コピーの場合は別々のリストを参照しています。

代入では同じオブジェクトを共有し、コピーでは別のオブジェクトが作られます。
その違いが is の結果に表れます。

▶ 浅いコピーと深いコピーの違いについて詳しく知りたい
Pythonの代入とコピーの違いとは?|copyとdeepcopyを理解する
Pythonの代入とコピーは何が違うのでしょうか。代入、浅いコピー、深いコピーの違いを、オブジェクトと参照の仕組みから図解。copyやdeepcopyの使い分けまで、具体例を使ってわかりやすく解説します。

5. id() を使うとオブジェクトの同一性を確認できる

Pythonには、オブジェクトの識別値を調べる id() という関数があります。

id() が返す整数は、オブジェクトが存在している間、そのオブジェクトを識別するために使われます。

たとえば、

x = [1, 2, 3]
y = x
z = x.copy()

print(id(x) == id(y))
print(id(x) == id(z))

結果は、

True
False

となります。

x と y は同じオブジェクトを参照しているため、id() の結果も同じです。一方、z はコピーによって作られた別のリストなので、識別値も異なります。

このことから、is はオブジェクトの同一性を判定していると確認できます。

ただし、id() の値をそのままメモリアドレスだと考えるのは正確ではありません。CPythonではメモリアドレスに対応することがありますが、Pythonの言語仕様として保証されているわけではありません。

id() はオブジェクトの識別値を返します。
同時に存在する2つのオブジェクトについて、識別値が同じなら同じオブジェクトです。

6. 整数や文字列の値の比較に is を使ってはいけない理由

ここまでの説明を読むと、「数値や文字列でも is を使えばよいのでは?」と思うかもしれません。

しかし、値が等しいかを調べたいときに is を使うべきではありません。

Pythonの処理系は、整数や文字列などのオブジェクトを再利用する場合があります。そのため、同じ値を表す変数が、偶然同じオブジェクトを参照することがあります。

たとえば、

x = 10
y = 10

print(x is y)

というコードは、処理系によって True になることがあります。

ただし、これは「数値が等しいなら is も True になる」という意味ではありません。オブジェクトの再利用は、Pythonの処理系や実行状況に依存するためです。

数値や文字列の値を比較するときは == を使います。
is の結果がたまたま True になっても、値の比較に利用してはいけません。

この違いは、Pythonの言語仕様と処理系の実装を分けて考えるうえでも重要です。

7. None の比較には is を使う

では、is はどのような場面で使うのでしょうか。代表的なのが、None との比較です。

None は「値がないこと」などを表すために使われる特別なオブジェクトで、Pythonでは1つだけ存在します。そのため、ある変数が None そのものを参照しているかを調べるときは、is None を使います。

result = None

if result is None:
    print("結果がありません")

実行結果は、

結果がありません

となります。

result == None と書くこともできますが、== はオブジェクトの型によって比較方法が変わる可能性があります。一方、is None は、result が None そのものかどうかを判定します。

None と is の関係

上の図では、result が特別なオブジェクト None を参照している様子を示します。

None との比較には is None、None でないことの確認には is not None を使います。

たとえば、値が設定されているかを確認する場合は、次のように書けます。

result = [1, 2, 3]

if result is not None:
    print("結果があります")

このように、is は特定のオブジェクトそのものを確認したいときに使います。

8. == と is を比較する

ここまでの違いを整理してみましょう。

比較 == is
調べるもの 値の等価性 オブジェクトの同一性
比較の基準 型ごとの比較方法 同じオブジェクトかどうか
別のリストでも内容が同じ場合 True False
同じリストを参照する場合 True True
主な用途 数値・文字列・リストなどの値の比較 None など特定オブジェクトの確認

重要なのは、== と is のどちらが優れているかではなく、何を比較したいのかによって使い分けることです。

値が等しいかを調べたいなら ==、同じオブジェクトかを調べたいなら is を使います。

9. 「同じオブジェクトなら値も等しい」と考えてよい?

ここで、もう少しだけ考えてみましょう。

同じオブジェクトを参照しているなら、通常は == も True になるように思えます。実際、ここまで扱ったリストの例では、x is y が True なら x == y も True でした。

ただし、一般的には必ずそうなるとは限りません。

たとえば、浮動小数点数には NaN という特殊な値があります。NaN は通常の数値とは異なり、自分自身と比較しても等しくないという性質を持っています。

x = float("nan")
y = x

print(x is y)
print(x == y)

結果は、

True
False

となります。

x と y は同じオブジェクトを参照しているため、is は True です。しかし、NaN の比較規則により、== は False になります。

is と == は独立した比較です。
同じオブジェクトであっても、等価性の比較結果が必ず True になるとは限りません。

このような例外があることも、2つの演算子を区別する理由の1つです。

10. == と is の違いを理解すると何が見えてくる?

ここまでの内容は、これまでの記事で学んできた考え方とつながっています。

最初の記事では、変数名とオブジェクトを分けて考えることを学びました。次の記事では、ミュータブルとイミュータブルの違いを確認しました。

さらに前回の記事では、代入すると同じオブジェクトを共有し、コピーすると別のオブジェクトが作られることを学びました。そして今回、== と is を使うことで、値の等しさとオブジェクトの同一性を区別できるようになりました。

変数名はオブジェクトを参照する
↓
代入では同じオブジェクトを共有する
↓
コピーでは別のオブジェクトを作る
↓
値の等しさは == で比較する
↓
オブジェクトの同一性は is で比較する

Pythonの比較を理解するには、「値」と「オブジェクト」を分けて考えることが重要です。

この視点は、リストなどのミュータブルなオブジェクトが複数の変数から参照されている場合にも役立ちます。

▶ ミュータブル・イミュータブルについて詳しく知りたい
Pythonのミュータブル・イミュータブルとは?|型ごとの違いを整理
Pythonのミュータブル・イミュータブルとは何かを初心者向けに解説。list、dict、set、int、str、tupleなどの型ごとの違いと、オブジェクトや参照、再代入との関係を図解しながらわかりやすく紹介します。

まとめ

Pythonの == と is について、今回のポイントをまとめます。

  • == は値の等価性を比較する
  • is はオブジェクトの同一性を比較する
  • 値が等しくても、別のオブジェクトなら is は False になる
  • 代入では同じオブジェクトを共有し、コピーでは別のオブジェクトを作る
  • 数値や文字列の値を比較するときは == を使う
  • None との比較には is None や is not None を使う
  • 同じオブジェクトでも、== が必ず True になるとは限らない

== は「値が等しいか」、is は「同じオブジェクトか」を比較する演算子です。

これまで、「変数と参照」→「ミュータブル・イミュータブル」→「代入とコピー」→「== と is」の順に見てきました。

次の記事では、これらの考え方を使って、なぜPythonのリストは別の変数から変更されることがあるのかを、参照の共有という視点から詳しく説明していきます。

関連記事

前回の記事:Pythonの代入とコピーの違いとは?|copyとdeepcopyを理解する

Pythonの代入とコピーの違いとは?|copyとdeepcopyを理解する
Pythonの代入とコピーは何が違うのでしょうか。代入、浅いコピー、深いコピーの違いを、オブジェクトと参照の仕組みから図解。copyやdeepcopyの使い分けまで、具体例を使ってわかりやすく解説します。

次回の記事:Pythonのリストはなぜ別の変数からも変更される?|参照の共有を理解する

(準備中)

関連書籍

Pythonの文法だけでなく、オブジェクトやデータモデルなど、Pythonの仕組みをもう少し深く理解したい人には『入門 Python 3』もおすすめです。

Amazon.co.jp

コメント

タイトルとURLをコピーしました