Python入門から応用までの学習サイト


== と is の違い

Pythonではオブジェクトを比較する方法として「==」や「is」があります。両者にはどういった違いがあるのか、また特定のオブジェクトがNoneであるかを判定する場合はどちらを用いるべきなのかを見てみましょう。

※本稿は少し内容が難しいため、基礎編としては飛ばしても構いません。取り急ぎ「None」は「is」で判定する、と覚えておきましょう。

「==」はオブジェクト同士が等価であるかを判定する演算子です。等価とは「同じ値かどうか」を指します。

# -*- coding:utf-8 -*-


test_list_1 = [100, 200, 300]
test_list_2 = [100, 200, 300]
test_list_3 = [300, 200, 100]

test_tuple_1 = (100, 200, 300)
test_str_1 = 'python-izm'


print test_list_1 == test_list_2
print test_list_1 == test_list_3
print '------------------------------'
print test_list_1 == test_tuple_1
print test_str_1 == 'python-izm'

--実行結果--

True
False
------------------------------
False
True

たとえばリストの場合「test_list_1」と「test_list_2」は別のオブジェクトですが、同じ値を同じ順番で保持しているため、「==」はTrue(等価)となります。「test_list_1」と「test_list_3」は同じ値を保持していますが、保持している順番が異なるため、「==」はFalse(等価ではない)になります。「test_list_1」と「test_tuple_1」は同じ値、同じ順番ですが型が異なるためFalseです。文字列の比較はわかりやすいと思います。


このように一口に「等価」といってもリストやタプル、文字列によって等価であるかどうかの判定にはある程度の幅があります。たとえば「test_list_1」と「test_tuple_1」は前述のように同じ値、同じ順番であるため、場合によってはTrueであった方が都合が良いときもあります。これを解決する方法として、Pythonでは演算子をオーバーロードする機能が備わっていますが、少し説明が複雑になってしまうので後述します。



オブジェクトを生成すると他のオブジェクトとは重複する事のない一意の番号を保持します。これはid関数で取得することができます。「is」演算子はこれを比較し、同じオブジェクトであるか(同一)を判断します。

# -*- coding:utf-8 -*-


test_list_1 = [100, 200, 300]
test_list_2 = [100, 200, 300]


print test_list_1 == test_list_2
print test_list_1 is test_list_2
print '---------------------------------------------'
print id(test_list_1)
print id(test_list_1) == id(test_list_1)
print test_list_1 is test_list_1

--実行結果--

True
False
---------------------------------------------
44842888 (※実行環境によって異なる番号が出力されます)
True
True

上記のように「test_list_1」と「test_list_2」は「==」ではTrue(等価)ですが、「is」ではFalse(同一ではない)です。これは両者が同じオブジェクトではないので「id」が異なるためです。つまり「id(obj_A) == id(obj_B)」がTrueであるとき、「obj_A is obj_B」は必ずTrueになります。



あるオブジェクトがNoneであるかを判定する場合は「is」演算子を用います。その理由として以下2点を挙げることができます。

  • 「==」よりも「is」の方が処理が速い
  • 「==」はオーバーロードできるため、想定通りの結果とならない場合がある


まず前提として、「None」はそれ自身が何もない状態を表すオブジェクトです。次のように「NoneType」であり「id」も持っています。

# -*- coding:utf-8 -*-

print type(None)
print id(None)

--実行結果--

<type 'NoneType'>
506046184 (※実行環境によって異なる番号が出力されます)


次はいわゆる通常のオブジェクトを作成してみましょう。当然のことながらそのオブジェクトも「id」を持ちます。

# -*- coding:utf-8 -*-


val = 'python-izm'
print id(val)

--実行結果--

39283616 (※実行環境によって異なる番号が出力されます)


さらにそのオブジェクトへ「None」を代入し、その「id」を見てみましょう。「None」を代入する前後で「id」が変わったのを確認することができます。

# -*- coding:utf-8 -*-


val = 'python-izm'
print id(val)

val = None
print id(val)

--実行結果--

39283616  (※実行環境によって異なる番号が出力されます)
506046184 (※実行環境によって異なる番号が出力されます)


文字列は更新不能なオブジェクトです。代入によって「id」が変わるのはそう珍しいことではありませんが、その「id」を「None」のものと比較してみます。

# -*- coding:utf-8 -*-


val = 'python-izm'
print id(val)

val = None
print id(val)

print id(None) == id(val)

--実行結果--

39283616  (※実行環境によって異なる番号が出力されます)
506046184 (※実行環境によって異なる番号が出力されます)
True


結果は「同一」なオブジェクトであると判定されました。つまり「None」が代入された時点で、すべてのオブジェクトは「Noneオブジェクト」として一括りにされます。これは「None」がシングルトンであるため、常に1つしか存在しないオブジェクトだからです。そのためオブジェクトが同一であるかを見る「is」演算子を用いて「None」かどうかを判定するのが正しい用法です。次は何故「==」を用いるべきではないのかを説明します。



まず「==」演算子はそのオブジェクト(クラス)が定義している「__eq__」メソッドの結果を見ます。「__eq__」が定義されていない場合は最上位の基底クラスである「object」で定義されている「__eq__」メソッドの結果を利用することになりますが、これは「is」と同じく「id」を比較しています。

# -*- coding:utf-8 -*-


class TestClass:
    pass


c_1 = TestClass()
c_2 = TestClass()

print c_1 == c_2
print c_1 is c_2

--実行結果--

False
False

つまり自ら定義したクラスなどでは独自の「__eq__」を定義しない限り、「==」と「is」は同じ結果となります(明示的に何かしらのクラスを継承した場合は異なる可能性があります)。ただし「==」の方は「object」クラスの「__eq__」メソッドを辿って結果を返し、なおかつその処理は「is」と同じことを行っているだけ(同じオブジェクトであればTrue)なので、直接「is」で比較をした方がより速く処理を行うことができます。


次は独自の「__eq__」メソッドを定義してみましょう。あまりないことですが、比較対象(other)がどうであろうと必ずTrueを返すことにします。

# -*- coding:utf-8 -*-


class TestClass:

    def __eq__(self, other):
        return True


c_1 = TestClass()

print c_1 == None
print c_1 is None

--実行結果--

True
False

結果は「==」でTrueとなり、「is」でFalseとなりました。「c_1」は「None」ではないので、想定通りの結果であるとはいえません。このようにオーバーロードで結果を操作することができるため、「==」で「None」を判定する場合、そのクラスが「__eq__」メソッドを定義しているか、定義している場合どのような処理を行っているかを把握しておく必要があります。そういった手間や処理速度などを考えると「is」を用いて「None」の判定を行うべきなのは明白です。




Python
スタートブック


入門 Python 3


Effective
Python


退屈なことは
Pythonにやらせよう

 
 
 

基礎編の仕上げにバージョン2系と3系の違いを学べ!

▶基礎編:2系と3系の違い



確かな力が身につく
Python「超」入門




P  R