Pythonのisと==の比較 – オブジェクトの比較

Pythonにおける’is’と’==’の比較

Pythonには、2つのオブジェクトが等しいかどうかをチェックするための非常によく似た2つの演算子があります。

この2つの演算子は is== です。

この2つの演算子は、int型とstring型のような単純なデータ型(多くの人がPythonを学び始めると思います)では、同じことをするように見えるので、通常混同されています。

x = 5
s = "example"


print("x == 5: " + str(x == 5))
print("x is 5: " + str(x is 5))
print("s == 'example': " + str(s == "example"))
print("s is 'example': " + str(s is "example"))


このコードを実行すると、次のような結果になります。

x == 5: True
x is 5: True
s == 'example': True
s is 'example': True


これは、 ==is がこれらのケースで同じ値 (True) を返していることを示しています。

some_list = [1]


print("some_list == [1]: " + str(some_list == [1]))
print("some_list is [1]: " + str(some_list is [1]))


という結果になります。

some_list == [1]: True
some_list is [1]: False


ここで、これらの演算子が同じでないことは明らかです。

この違いは、isが(オブジェクトの)同一性をチェックするのに対し、==は(値の)等質性をチェックすることから来ています。

この2つの演算子の違いを明確にするために、もう1つの例を挙げます。

some_list1 = [1]
some_list2 = [1]
some_list3 = some_list1


print("some_list1 == some_list2: " + str(some_list1 == some_list2))
print("some_list1 is some_list2: " + str(some_list1 is some_list2))
print("some_list1 == some_list3: " + str(some_list1 == some_list3))
print("some_list1 is some_list3: " + str(some_list1 is some_list3))


この結果、次のようになります。

some_list1 == some_list2: True
some_list1 is some_list2: False
some_list1 == some_list3: True
some_list1 is some_list3: True


見ての通り、 some_list1some_list2 は値としては同じですが(どちらも [1]] と同じ)、同一ではない、つまり、値は同じでも同じオブジェクトではない、ということです。

しかし、 some_list1some_list3 とメモリ上の同じオブジェクトを参照しているので、等しく、かつ同一です。

MutableとImmutableのデータ型

この問題は(変数に名前がついていれば)クリアできるかもしれませんが、別の疑問が浮かんでくるかもしれません。

という疑問が浮かんでくるかもしれません。

Pythonのデータ型には、mutableとimmutableの2種類があります。

  • Mutableデータ型は、時間の経過とともに「変更」することができるデータ型です。
  • Immutable データ型は、一度作成されると、同じメモリ位置に留まります。

Mutable データ型は以下の通りです。

List,dictionary,set`, そしてユーザー定義クラスです。

不変のデータ型は以下の通りである。

int,float,decimal,bool,string,tuple, そしてrange` です。

他の多くの言語と同様に、Python は不変データ型に対して、 変動データ型とは異なる処理を行います。

つまり、メモリに一度だけ保存します。

つまり、メモリに一度だけ保存されます。

そのため、コード内で使用するすべての 5 は、コード内の他の場所で使用する 5 と全く同じものです。

文字列 "example" を一度使うと、他の場所で "example" を使うたびに、まったく同じオブジェクトになります。

さらに詳しい説明はこのNoteを参照してください。

ここでは、各オブジェクトに一意な識別子を出力する id() というPythonの関数を使って、このミュータビリティの概念を実際に使って詳しく見ていくことにします。

s = "example"
print("Id of s: " + str(id(s)))
print("Id of the String 'example': " + str(id("example")) + " (note that it's the same as the variable s)")
print("s is 'example': " + str(s is "example"))


print("Change s to something else, then back to 'example'.")
s = "something else"
s = "example"
print("Id of s: " + str(id(s)))
print("s is 'example': " + str(s is "example"))
print()


list1 = [1]
list2 = list1
print("Id of list1: " + str(id(list1)))
print("Id of list2: " + str(id(list2)))
print("Id of [1]: " + str(id([1])) + " (note that it's not the same as list1!)")
print("list1 == list2: " + str(list1 == list2))
print("list1 is list2: " + str(list1 is list2))


print("Change list1 to something else, then back to the original ([1]) value.")
list1 = [2]
list1 = [1]
print("Id of list1: " + str(id(list1)))
print("list1 == list2: " + str(list1 == list2))
print("list1 is list2: " + str(list1 is list2))


これは次のように出力されます。

Id of s: 22531456
Id of the String 'example': 22531456 (note that it's the same as the variable s)
s is 'example': True
Change s to something else, then back to 'example'.
Id of s: 22531456
s is 'example': True


Id of list1: 22103504
Id of list2: 22103504
Id of [1]: 22104664 (note that it's not the same as list1!)
list1 == list2: True
list1 is list2: True
Change list1 to something else, then back to the original ([1]) value.
Id of list1: 22591368
list1 == list2: True
list1 is list2: False


この例の最初の部分では、たとえ s の値を変更したとしても、 s は最初に割り当てられた "example" オブジェクトと全く同じものを返していることがわかります。

しかし、list は値が [1] である同じオブジェクトを返すのではなく、最初の [1] と同じ値であっても、全く新しいオブジェクトが生成されます。

上記のコードを実行すると、オブジェクトのIDは異なるものの、等号は同じになる可能性が高いです。

is’ と ‘==’ はいつ使い分けられるのか?

is演算子はオブジェクトとNone` を比較するときに最もよく使われます。

本当に (本当に) 2 つのオブジェクトが同じかどうかをチェックしたいのでなければ、この特定のシナリオに限定して使用することが一般的に推奨されます。

さらに、 is はメモリアドレスが整数で等しいかどうかをチェックするだけなので、一般的に == 演算子よりも高速に処理することができます。

重要な注意事項: is が期待通りに動作する唯一の状況は、( None のような)シングルトン・クラスやオブジェクトの場合です。

イミュータブルなオブジェクトであっても、 is が期待通りに動作しない場合があります。

例えば、コードロジックで生成された大きな string オブジェクトや、大きな int では、 is は予測できない振る舞いをすることがあります(しそうです)。

インターン(つまり、 string/int/etc. のコピーが1つだけ存在することを絶対に確認する) を行う努力をしない限り、使用予定の様々な不変オブジェクトすべてにおいて、 is は予測不可能な振る舞いをすることになります。

結論は、99%のケースで == を使用することです。

2つのオブジェクトが同一であれば、それはまた等しく、その逆は必ずしも正しくないということです。

‘==’ と ‘!=’ 演算子のオーバーライド

演算子 !=is not は、対応する “正” の演算子と同じように動作します。

すなわち、 != はオブジェクトが同じ値を持たない場合に True を返し、 is not はオブジェクトが同じメモリアドレスに保存されていない場合に True を返します。

この二つの演算子のもう一つの違いは、カスタムクラスでは ==/!= の動作をオーバーライドすることができますが、 is の動作はオーバーライドすることができないことです。

もし、カスタムメソッド __eq()__ をクラスに実装すれば、 ==/!= 演算子の振る舞いを変更することができます。

class TestingEQ:
    def __init__(self, n):
        self.n = n


# using the '==' to check whether both numbers
    # are even, or if both numbers are odd
    def __eq__(self, other):
        if (self.n % 2 == 0 and other % 2 == 0):
            return True
        else:
            return False


print(5 == TestingEQ(1))
print(2 == TestingEQ(10))
print(1 != TestingEQ(2))


この結果、以下のようになります。

False
True
True


結論

要するに、 ==/!= は(値によって)等しいかどうかをチェックし、 is/is not は2つのオブジェクトが同じかどうか、つまりメモリアドレスをチェックするものである。

しかし、 is は何をやっているのかよくわからない場合や、 None のようなシングルトンオブジェクトを扱う場合には、予想外の動作をする可能性があるため、使用しないようにしましょう。

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