(ob1 is ob2) は (id(ob1) == id(ob2)) と同等です
まず、2 つのオブジェクトのメモリ アドレスが同じ場合、id 関数はオブジェクトのメモリ アドレスを取得できます。 2 つのオブジェクトはオブジェクトである必要があります。はと同等です。証拠としての Python ソースコード。
static PyObject * cmp_outcome(int op, register PyObject *v, register PyObject *w) { int res = 0; switch (op) { case PyCmp_IS: res = (v == w); break; case PyCmp_IS_NOT: res = (v != w); break;
しかし、以下のコードでこの状況がどのように発生するかを見てください。
[1] の場合: def bar(self, x):
...: return self.x + y
...:
[2] の場合: class Foo(object):
.. .: x = 9
...: def __init__(self ,x):
...: self.x = x
...: bar = bar
...:
In [3 ]: foo = Foo(5)
In [4]: foo.bar は Foo.bar
Out[4]: False
In [5]: id(foo.bar) == id(Foo .bar )
Out[5]: True
isでは2つのオブジェクトがFalseと判定されるが、idではTrueとなる これは私たちが知っている事実と矛盾します。この状況に対する最善の解決策は、dis モジュールを呼び出して 2 つの比較ステートメントが何を行うかを確認することです。
[7]: dis.dis("id(foo.bar) == id(Foo.bar)")
6
7 DELETE_GLOBAL 29281 (29281) S 10 Store_sLice+111 Slice+ 2
12 delete_subscr
13 delete_subscr
14 スライス+2
15built_map 10340
18 prINT_EXPR
19 Jump_if_orse_pop 118877 E 22 削除_global 29281 (29281)
25 STORE_SLICE+1
In [ 8]: dis.dis("foo.bar は Foo.bar")
0 BUILD_TUPLE 3
4 DELETE_GLOBAL 29281 (29281) 7 SLICE+2 - 5 TE_GLOBAL 29281 (29281) 本当の状況は. 演算子が実行されると、実際にはプロキシ オブジェクトが生成されます。 foo.bar が Foo.bar の場合、2 つのオブジェクトが順番に生成され、スタック上で比較されます。 アドレスが異なるため、False になるはずですが、異なります。 id(foo.bar) == id(Foo.bar) の場合、最初に Foo.bar が生成され、次に foo.bar のアドレスが計算されます。foo.bar のアドレスを計算すると、foo を指すオブジェクトは存在しません。 bar なので、foo.bar オブジェクトが解放されます。次に、Foo.bar オブジェクトを生成します。 foo.bar と Foo.bar は同じメモリ サイズを占有するため、元の foo.bar のメモリ アドレスが再利用されるため、 id(foo.bar) == id(Foo.bar )は真です。 以下の内容は、Leo Jay によってよりわかりやすく説明されています。id(式a) == id(式b)を使って2つの式の結果が同じオブジェクトかどうかを判定するという考え方には問題があります。
foo.bar この形式は属性参照[1]と呼ばれるもので、式の一種です。 foo はインスタンスオブジェクト、bar はメソッドです。このとき、式 foo.bar によって返される結果をメソッドオブジェクトと呼びます [2]。ドキュメントによると:
データ属性ではないインスタンス属性が参照されると、
そのクラスが検索され、その名前が関数オブジェクトである有効なクラス属性を示す場合、
メソッドオブジェクトが作成されます。
(pointers to) the instance object and the function object just found
together in an abstract object: this is the method object.
foo.bar 自体は単純な名前ではなく、式の計算結果です。メソッド オブジェクト。 id(foo.bar) などの式では、メソッド オブジェクトは単なる一時的な中間変数です。一時的な中間変数に id を使用することは意味がありません。
よりわかりやすい例は、
print id(foo.bar) == id(foo.__init__) です
出力結果も True です
id ドキュメント [3] を見てください:
Return the “identity”これはオブジェクトの整数 (または長整数
) です。integer) は、このオブジェクトの存続期間中、
一意で一定であることが保証されます。重複しない存続期間を持つ 2 つのオブジェクトは、同じ id() 値を持つ可能性があります
CPython 実装の詳細: これは、 object in Memory.
オブジェクトが破壊されないことが保証できる場合にのみ、id を使用して 2 つのオブジェクトを比較できます。したがって、比較する必要がある場合は、次のように書かなければなりません:
fb = foo.bar
Fb = Foo.bar
print id(fb) == id(Fb)
つまり、次の結果です。 2 つの式 名前にバインドし、それらが同じオブジェクトであるかどうかを比較することによってのみ、正しい結果を得ることができます。
is 式 [4] についても同様です。現在得られる正しい結果は、完全に CPython の現在の実装の詳細によるものです。現在の is の実装では、左側と右側のオブジェクトを計算し、2 つのオブジェクトのアドレスが同じかどうかを比較します。いつか変更した場合は、まず左辺を計算してアドレスを保存し、左辺を解放してから右辺を計算し、再度比較すると結果が間違っている可能性があります。この問題は公式ドキュメント [5] にも記載されています。正しい方法は id のように、まず左辺と右辺を計算し、それぞれの名前に明示的にバインドしてから is を使用して判断する方法だと思います。