Last Updated: 2/6/2024, 5:44:57 AM

# is と == の違い




4.3. 比較 - Python 標準ライブラリ
is
オブジェクトが同じであるかどうかを判定する比較演算子
==
オブジェクトの値が等しいかどうかを判定する比較演算子

is も == も、ともに比較演算子です。

comp_operator ::=  "<" | ">" | "==" | ">=" | "<=" | "!="
                   | "is" ["not"] | ["not"] "in"
6.10. Comparisons - The Python Language Reference

# 1. 比較演算子 is

2 つの変数に代入されたオブジェクトが  同じであれば  True を返します。

a = [1, 2, 3]
b = a
c = [1, 2, 3]

a is b  # True  ... 同じオブジェクト
c is a  # False ... 別のオブジェクト

is 比較演算子は 2 つの変数に代入されたオブジェクトの identity, 同一性が  同じであれば  True を返します。 次の2つの式は、等価です。

a is b
id(a) == id(b)

6.10.3. 同一性の比較 - Python 言語リファレンス (opens new window)
isis not 演算子はオブジェクトの同一性 identity について試験します: x と y が同じオブジェクトである場合のみ、 x is y は真を返します。 オブジェクトの同一性 identity は id() 関数を使って確認できます。x is not y は、反対の真偽値を返します。[4]
The operators is and is not test for object identity: x is y is true if and only if x and y are the same object. Object identity is determined using the id() function. x is not y yields the inverse truth value.[4]

オブジェクトの同一性, identity とは オブジェクトに1つ1つ割り当てられた背番号のようなものです。 id 関数で調べることができます。

a = [1, 2, 3]
b = a
c = [1, 2, 3]

id(a)
id(b)
id(c)
>>> id(a)  <-- 変数 a と
4360540680
>>> id(b)
4360540680
>>> id(c)  <-- 変数 c には同じ identity がはいっています。
4360555080
>>> 

そうです。実は変数にはオブジェクトがはいっているのではなく、  identity という数字、背番号が保存されています。  そのことについては以下の記事で説明させていただきました。




# 2. 比較演算子 ==

簡単に言えば == は、値が等しいかどうかを確認しています。

正確に言えば == は、値が等しいかどうかを確認するように実装されていて、 その実装のされ方はクラスごとに異なります。



4.3. 比較 - Python 標準ライブラリ
演算意味
==等しい

# ◯ 値

Python でと言うと、オブジェクトの属性に代入されたオブジェクト、 または list, tuple, str などのシーケンスに代入されたオブジェクトを指すことがような気がします。

# オブジェクトの値
class C:
    pass

obj = C()
obj.a = 100

# シーケンスのオブジェクトの値
seq = "Hello, world!"


obj.a
seq[0]

>>> obj.a
100
>>> seq[0]
'H'
>>>

# ◯ 簡単に言えば

値が等しいかどうかを確認しています。

a = [1, 2, 3]
b = [1, 2, 3]

# a と b の値は同じ
a == b


# a に 4 を加えると同じ値ではなくなる
a.append(4)
a != b

# ◯ 正確には言えば

値が等しいかどうかを確認するように実装されていて、 その実装のされ方はクラスごとに異なります。

つまり == は値が同じであるかどうかを判定しているわけではありません。 値が同じでも False が返される例を見てみましょう。

class C:
    pass

obj1 = C()
obj1.a = 100

obj2 = C()
obj2.a = 100

# 値が同じでも False が返されています。 
# そもそもこいつは一体何を比較しているのでしょうか?
obj1 == obj2
e

では == はどのように実装されているのでしょうか。このあと具体例を3つ見ていきたいと思います。

# 実装例 1. ユーザが定義したクラス

実はユーザが定義したクラスは __eq__ メソッドを定義することで == 演算子を実装することができます。 反対に is 演算子はできません。こういうのをオペレータオーバーロード, operator overload なんて表現されたりもします。

"""矩形の等号 == を表現する書き方について考える."""


def sample_code():
    print(Rectangle(1, 1, 2, 2) == Rectangle(1, 1, 2, 2))
    print(eq(Rectangle(1, 1, 2, 2), Rectangle(1, 1, 2, 2)))


# こんな矩形のクラス
class Rectangle:
    def __init__(self, x1, y1, x2, y2):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2

    # identity が等しいかよりも
    # 値が等しいかの方が理解しやすいコードになる。
    # だから正確には違うけど
    # == は値が等しいかどうかの判定と書きました。
    def __eq__(self, other):
        return \
            self.x1 == other.x1 and \
            self.y1 == other.y1 and \
            self.x2 == other.x2 and \
            self.y2 == other.y2


def eq(rectangle_a, rectangle_b):
    return \
        rectangle_a.x1 == rectangle_b.x1 and \
        rectangle_a.y1 == rectangle_b.y1 and \
        rectangle_a.x2 == rectangle_b.x2 and \
        rectangle_a.y2 == rectangle_b.y2


if __name__ == '__main__':
    sample_code()

その他にも実装できる比較演算子があります。 実際にこの次のページで実装していきます。

3.1. オブジェクト、値、および型 (opens new window)
object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)

これらはいわゆる "拡張比較 (rich comparison)" メソッドです。
演算子シンボルとメソッド名の対応は以下の通りです:
x < y は x.__lt__(y) を呼び出します;
x <= y は x.__le__(y) を呼び出します;
x==y は x.__eq__(y) を呼び出します;
x!=y は x.__ne__(y) を呼び出します;
x > y は x.__gt__(y) を呼び出します;
x>=y は x.__ge__(y) を呼び出します。

is はできません。

4.3. 比較 (opens new window)
is および is not 演算子の振る舞いはカスタマイズできません。

# 実装例 2. object クラス

組込型である object クラスに適用される == 演算子の動作は is と同じで、 同じオブジェクトであるかどうかを比較しているだけです。

# なんと object クラスのインスタンスにも
# == 演算子が使える。
# 中身は identity を比較しているだけ。
object() == object() 

# identity が等しいならば True を返す。
obj = object()
obj == obj

>>> object() == object() 
False
>>>
>>> obj == obj
True
>>> 

等価性比較 (== および !=) のデフォルトの振る舞いは、オブジェクトの同一性に基づいています。 従って、同一のインスタンスの等価性比較の結果は等しいとなり、 同一でないインスタンスの等価性比較の結果は等しくないとなります。 デフォルトの振る舞いをこのようにしたのは、全てのオブジェクトを反射的 (reflexive つまり x is y ならば x == y) なものにしたかったからです。
6.10.1. 値の比較 - Python 言語リファレンス (opens new window)

ここで上の方で紹介した値が同じでも False が返される例についても動作が理解できます。

>>> class C: 
...     pass
... 
>>> 
>>> 
>>> obj1 = C()
>>> obj1.a = 100
>>> 
>>> obj2 = C()
>>> obj2.a = 100
>>> 
>>> # object クラスの == の処理が呼び出される
>>> # identity が異なるので False が返されている。
>>> obj1 == obj2
False
>>>
>>> # identity が同じなら true が返される。
>>> obj1 == obj1
True

Python のハッシュと同値性 (opens new window)
Python Hashes and Equality

Python における等式 == は大抵の人が認識しているよりも複雑ですが、 根本的には __eq__(self, other) メソッドを実装しなければならないということです。
Equality in Python is more complicated than most people realize but at its core you have to implement a __eq__(self, other) method.

中略
(Omitted)

クラス定義時に継承するオブジェクトを指定しなければ、 これらの __eq__(self, other), __ne__(self, other) などのメソッドは object クラスから継承されます。object クラスで定義されているメソッドでは2つのインスタンスを identity 同一性によって比較します。つまり2つのインスタンスが同じものである時のみ、等しいと判断します。
By default, those methods are inherited from the object class that compares two instances by their identity – therefore instances are only equal to themselves.

あまりちゃんと理解できていませんが、 実際に cpython の object クラスの == の実装を見てみます。

static PyObject *
object_richcompare(PyObject *self, PyObject *other, int op)
{
    PyObject *res;

    switch (op) {

    case Py_EQ:
        /* Return NotImplemented instead of False, so if two
           objects are compared, both get a chance at the
           comparison.  See issue #1393. */
        res = (self == other) ? Py_True : Py_NotImplemented;
        Py_INCREF(res);
        break;

    ... 省略

cpython/typeobject.c (opens new window)

self, other にはオブジェクトへのポインタが格納されています。 CPython で self == other とは Python で言えば self is other みたいなものです。

        res = (self == other) ? Py_True : Py_NotImplemented;
        // Python のコードに無理やり書き換えるなら...
        // res = True if self is other else NotImplemented

でも、等しければ True(Py_True) はわかるのですが、 なぜ等しくないときは Py_NotImplemented を返してるのでしょうか? 理解してないですが。Py_False を返してしまうと期待した動作をしてくれないみたいです.. 詳細はリンク先をどうぞ。

# 実装例 3. list クラス

list オブジェクトの == の実装。リスト内の全ての要素を比較したりしている様子が見えます。

static PyObject *
list_richcompare(PyObject *v, PyObject *w, int op)
{
    PyListObject *vl, *wl;

    ... 省略

    vl = (PyListObject *)v;
    wl = (PyListObject *)w;

    ... 省略

    /* 先頭から見て最初に要素が異なるインデックスを探す */
    for (i = 0; i < Py_SIZE(vl) && i < Py_SIZE(wl); i++) {
        int k = PyObject_RichCompareBool(vl->ob_item[i],
                                         wl->ob_item[i], Py_EQ);
        if (k < 0)
            return NULL;
        if (!k)
            break;
    }

    ... 省略
cpython/listobject.c

どうやって組み込み関数のソースに当たればいいかは、 stackoverflow 先生が教えてくれました。

# 3. singleton を比較するときは is

singleton を比較するときは == ではなく is を使うように PEP 8 で定められています。

# OK
if x is None:
    ...

# NG
if x == None:
    ...

None のような singleton を比較するときは、 必ず is もしくは is not を使ってください。 決して == 演算子を使ってはいけません。
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
Programming Recommendations - PEP 8 (opens new window)

# ◯ singleton, シングルトン ってなに?

シングルトンとは、1つだけしかインスタンスを生成しないクラス、 またはそのインスタンスを指しています。 Python では None, Ture, False が singleton です。 他にもあるかもしれません。

試しに None を 10000 回インスタンス化しましたが、 すべて同じ None が返されます。 all 関数は次の記事でご紹介するので、 適当に、ふーんと流していただけると幸いです。

all(None is type(None)() for _ in range(10000))
>>> all(None is type(None)() for _ in range(10000))
True
>>>

基本的にシングルトンパターンは、あまりよくない実装らしいです。 None, True, False など、分割できない原始的なオブジェクトには使ってもいいと思うのですが (すいません、いい表現が見つかりません)。 class を使ったユーザ定義クラスなどで実装する方法もあるのですが、 自分で実装はしない、使わない方が望ましいかなと感じたりもします。

# ◯ is を使う理由

このような規則があるのは、次の2つの理由からだと考えます。

まず第一に、== という演算子は is よりも条件が緩い演算子です。 例えば型が違っても True を返すことがあります。 0 == 0.0 など。 singleton に対するより正確な動作を期待するなら is を使うのはいいのかなと思います。

また第二に、a is None という書き方のほうが普通の言葉、 英語に近いから採用されたのではないかなと考えています。

# ◯ is に対する違和感

PEP 8 に記載されているので従ってはいますが。この書き方については、個人的に疑問を感じています。

まず第一に、我々がしたいのはあくまでも同値性 == の比較であって同一性 is の比較ではありません。 確かに == は is よりも条件が緩い演算子ですが、意図してないものを書かせるのはおかしい気がします。 これは、たとえ表現が英語に近くなるからとしてもです。

また第二に、int や long の違いについては Python の実装のされ方を意識せずにコーディングしてるのに、 None が singleton であるかどうかについては Python の実装のされ方を意識してコーディングしないといけないというのは、 ちょっと不思議な感じです。

さらに言えば PEP 8 には書いてありますが、 公式ドキュメントで None が singleton だと書いてある箇所が見つかりません。 PEP 8 も、もちろん公式ドキュメントですが PEP 8 は、コーディング規約について書かれた文章ですが、 言語仕様に関する文章ではありません。

None
型 NoneType の唯一の値です。 None は、関数にデフォルト引数が渡されなかったときなどに、 値の非存在を表すのに頻繁に用いられます。 None への代入は不正で、SyntaxError を送出します。 (singleton とは書かれていない)
3. 組み込み定数 - 標準モジュール (opens new window)

# True と False

同じ PEP 8 でも None には is 使うなと言ってるのに True, False には、もっとダメ worse と言っています。

真偽値を == を使って True, False と比較しないでください。
Don't compare boolean values to True or False using ==.

Yes:   if greeting:
No:    if greeting == True:
Worse: if greeting is True:

Style Guide for Python Code - PEP 8 (opens new window)

True と False が singleton であるにも関わらず。

False と True は singleton になります、None のように。 bool 型は、この2つの値しか持たないので、もしかしたらこれらは doubletons と呼ばれるべきかもしれませんね? 現実の実装では、True, False 以外の bool 型のインスタンスが生成されることを許しません。
The values False and True will be singletons, like None. Because the type has two values, perhaps these should be called "doubletons"? The real implementation will not allow other instances of bool to be created.
Specification - Adding a bool type - PEP 285 (opens new window)

もちろん greeting == True も greeting is True もよくない書き方であることは解るのですが、 「singleton では is を使ってね」と言っていたのに 「greeting == True よりも greeting is True の方が worse より悪い書き方だよ」というのは いくらか矛盾があるかなと思います。

これは恐らく PEP 8 のこの項目を書く時に bool が singleton であるということが意識から抜けていたのではないでしょうか。 結局 singleton であるかどうか、Python の言語の実装を意識したコーディング規約というのは、 間違っているのではないかなと思ったりもします。

下記のブログで議論された英文のブログが紹介されています。

# おわりに

is と == の違いを見てきました。 書籍などでは is のことを難しい言葉で「同一性」の比較、 == のことを「同値性」の比較とか言ってたりしますので、 頭の片隅に置いておいていただいてもいいかなと感じたりもします。

以上になります。ありがとうございました。