Python の is と == の動作と違い
オブジェクトが同じであるかどうかを判定する比較演算子 | |
オブジェクトの値が等しいかどうかを判定する比較演算子 | |
正確には値が同じであるかどうかを確認するように実装されて、 あるいはしなければならず、その実装はクラスごとに異なります。 |
is も == も、ともに比較演算子です。
comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!=" | "is" ["not"] | ["not"] "in"
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. 同一性の比較
is
と is not 演算子はオブジェクトの同一性 identity について試験します: x と y が同じオブジェクトである場合のみ、x is y
はtrue
を返します。オブジェクトの同一性 identity は id() 関数を使って確認できます。x is not y は、反対の真偽値を返します。[4]
The operatorsis
and is not test for object identity:x is y
istrue
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)
>>> a = [1, 2, 3] >>> b = a >>> c = [1, 2, 3] >>> >>> id(a) 4360540680 >>> id(b) 4360540680 >>> id(c) 4360555080 >>>
Python の id 関数, identity ってなに? - いっきに Python に詳しくなるサイト
2. 比較演算子 ==
簡単に言えば == は、値が等しいかどうかを確認しています。
正確に言えば == は、値が等しいかどうかを確認するように実装されていて、その実装のされ方はクラスごとに異なります。
演算 | 意味 |
---|---|
== | 等しい |
◯ 値
Python で値と言うと、オブジェクトの属性に代入されたオブジェクト、または list, tuple, str などのシーケンスに代入されたオブジェクトを指すことが多いです。
>>> # オブジェクトの値 >>> class C: pass ... >>> obj = C() >>> obj.a = 100 >>> obj.a 100 >>> >>> # シーケンスオブジェクトの値 >>> s = "Hello, world!" >>> s[0] 'H'
Python で mutable と immutable の違い
◯ 簡単に言えば
値が等しいかどうかを確認しています。
>>> a = [1, 2, 3] >>> b = [1, 2, 3] >>> >>> # a と b の値は同じ >>> a == b True >>> >>> # a に 4 を加えると同じ値ではなくなる >>> a.append(4) >>> a == b False >>>
◯ 正確には言えば
値が等しいかどうかを確認するように実装されていて、その実装のされ方はクラスごとに異なります。
つまり == は値が同じであるかどうかを判定しているわけではありません。値が同じでも False が返される例を見てみましょう。
>>> class C: pass ... >>> >>> >>> obj1 = C() >>> obj1.a = 100 >>> >>> obj2 = C() >>> obj2.a = 100 >>> >>> # 値が同じでも False が返されています。 >>> # そもそもこいつは一体何を比較しているのでしょうか? >>> obj1 == obj2 False
では == はどのように実装されているのでしょうか。このあと具体例を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(object): def __init__(self, x1, y1, x2, y2): self.x1 = x1 self.y1 = y1 self.x2 = x2 self.y2 = y2 # id が等しいかよりも # 値が等しいかの方が理解しやすいコードになる。 # だから正確には違うけど # == は値が等しいかどうかの判定と書きました。 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()
その他にも実装できる比較演算子がありますが
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 はできません。
実装例 2. object クラス
組込型である object クラスに適用される == 演算子の動作は is と同じで、同じオブジェクトであるかどうかを比較しているだけです。
>>> # なんと object クラスのインスタンスにも >>> # == 演算子が使える。 >>> # 中身は identity を比較しているだけ。 >>> object() == object() False >>> >>> # identity が等しいならば true を返す。 >>> obj = object() >>> obj == obj True >>>
等価性比較 (== および !=) のデフォルトの振る舞いは、オブジェクトの同一性に基づいています。 従って、同一のインスタンスの等価性比較の結果は等しいとなり、同一でないインスタンスの等価性比較の結果は等しくないとなります。 デフォルトの振る舞いをこのようにしたのは、全てのオブジェクトを反射的 (reflexive つまり x is y ならば x == y) なものにしたかったからです。
6.10.1. 値の比較
ここで上の方で紹介した値が同じでも 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 における等式 == は大抵の人が認識しているよりも複雑ですが、根本的には __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; ... 省略
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 を返してしまうと期待した動作をしてくれないみたいです.. 詳細はリンク先をどうぞ。
Issue 1393: function comparing lacks NotImplemented error - Python tracker
実装例 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; } ... 省略
どうやって組み込み関数のソースに当たればいいかは、stackoverflow 先生が教えてくれた。
Finding the source code for built-in Python functions?
singleton を比較するときはに is を使う
# 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
◯ 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. 組み込み定数 - 標準モジュール
◯ 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
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
もちろん greeting == True も greeting is True もよくない書き方であることは解るのですが、
「singleton では is を使ってね」と言っていたのに
「greeting == True よりも greeting is True の方が worse より悪い書き方だよ」というのは
いくらか矛盾があるかなと思います。
これは恐らく PEP 8 のこの項目を書く時に bool が singleton であるということが意識から抜けていたのではないでしょうか。 結局 singleton であるかどうか、Python 側の実装を意識したコーディング規約というのは、間違っているのではないかなと思ったりもします。
下記のブログで議論された英文のブログが紹介されています。
Pythonでは None の比較は == ではなく is を使う
◯ if 文あるいは bool 型の設計について
基本的に if 文はあるか、ないかを判定しています。
したがって a が None であるかどうかを判定するのに not a ではなく a is None と強制するのはおかしいのではないかなと思ったりもします。
Python の if 文ってなに? - いっきに Python に詳しくなるサイト
おわりに
== は、オブジェクトの値が等しいかどうかを判定する比較演算子と公式マニュアルで書かれているのをご紹介しました。 ところで「値」とはなんでしょうか?
演算 | 意味 |
---|---|
== | 等しい |