(ob1 is ob2) 等價於(id(ob1) == id(ob2))
首先id函數可以獲得物件的記憶體位址,如果兩個物件的記憶體位址是一樣的,那麼這兩個物件肯定是一個對象。和is是等價的。 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;
但是請看下邊程式碼的這種情況怎麼會出現呢?
In [1]: def bar(self, x):
...: return self.x + y
...:
In [2]: class Foo(object):
In [2]: class Foo(object):
.. .: x = 9
...: def __init__(self ,x):
...: self.x = x
...: bar = bar
...:
In [3 ]: foo = Foo(5)
In [4]: foo.bar is Foo.bar
Out[4]: False
In [5]: id(foo.bar) == id(Foo.bar )
Out[5]: True
兩個物件用is判斷是False,用id判斷卻是True,這與我們已知的事實不符啊,這種現象該如何解釋呢?遇到這種情況最好的解決方法就是呼叫dis模組去看下兩個比較語句到底做了什麼。
In [7]: dis.dis("id(foo.bar) == id(Foo.bar)")
0 BUILD_MAP . 527
6
7 DELETE_GLOBAL 7 DELETE_GLOBAL 29281)
10 STORE_SLICE+1
11 SLICE+2 🜠 13 DELETE_SUBSCR
14 SLICE+2
18 PRINT_EXPR
19 JUMP_IF_FALSE_OR_POP 11887
_ 25 STORE_SLICE+1
In [8]: dis.dis("foo.bar is Foo.bar")
0 BUI_T
0 BUI
4 DELETE_GLOBAL 29281 (29281)
8 BUILD_MAP 8307
11 PRINT_EXPR SE 1887
15 DELETE_GLOBAL 29281 (29281) 真人的時候,兩個物件順序生成,放在堆疊裡相比較,由於地址不同肯定是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)的結果是True。 下面內容由郵件Leo Jay大牛提供,他解釋的更加通透。 用id(expression a) == id(expression b)來判斷兩個表達式的結果是不是同一個物件的想法是有問題的。 foo.bar 這種形式叫 attribute reference [1],它是表達式的一種。 foo是一個instance object,bar是一個方法,這個時候表達式foo.bar回傳的結果叫做method object [2]。依文件:When an instance attribute is referenced that isn't a data attribute, its class is searched. If the name denotes a valid class attribute 🜥(pointers to) the instance object and the function object just found together in an abstract object: this is the method object. foo.bar. method object,在id(foo.bar)這樣的表達式裡,method object只是一個暫時的中間變數而已,對暫時的中間變數做id是沒有意義的。 一個更明顯的例子是,print id(foo.bar) == id(foo.__init__) 輸出的結果也是True 㟎〜的文件[33]」 of an object. This is an integer (or long
integer) which is guaranteed to be unique and constant for this object
during its lifetime. Two objects with non-overlap lifetimes may
have the sameation ipping) ress of the object in memory. 只有你能保證物件不會被銷毀的前提下,你才能用id 來比較兩個物件。所以,如果你非要比的話,得這樣寫:fb = foo.bar Fb = Foo.bar print id(fb) == id(Fb) 綁定到名字上,再來比是不是同一個對象,你才能得到正確的結果。 is表達式 [4] 也是一樣的,你現在得到了正確的結果,完全是因為 CPython 現在的實作細節決定的。現在的is的實現,是左右兩邊的物件都計算出來,然後再比較這兩個物件的位址是否一樣。萬一哪天改成了,先算左邊,保存地址,把左邊釋放掉,再算右邊,再比較的話,你的is的結果可能就錯了。官方文件裡也提到了這個問題 [5]。我認為正確的方法也是像id那樣,先把左右兩邊都計算下來,並顯式綁定到各自的名字上,然後再用is判斷。