(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):...: x = 9...: def __init__(self ,x):...: self.x = x...: bar = bar...: In [3]: foo = Foo(5) In [4]: foo.bar is Foo.barOut[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 10340 3 BUILD_TUPLE 28527 6 <46> 7 DELETE_GLOBAL 29281 (29281) 10 STORE_SLICE+1 11 SLICE+2 12 DELETE_SUBSCR 13 DELETE_SUBSCR 14 SLICE+2 15 BUILD_MAP 10340 18 PRINT_EXPR 19 JUMP_IF_FALSE_OR_POP 11887 22 DELETE_GLOBAL 29281 (29281) 25 STORE_SLICE+1 In [8]: dis.dis("foo.bar is Foo.bar") 0 BUILD_TUPLE 28527 3 <46> 4 DELETE_GLOBAL 29281 (29281) 7 SLICE+2 8 BUILD_MAP 8307 11 PRINT_EXPR 12 JUMP_IF_FALSE_OR_POP 11887 15 DELETE_GLOBAL 29281 (29281)
真實情況是當執行.操作符的時候,實際是生成了一個proxy對象,foo.bar is Foo.bar的時候,兩個對象順序生成,放在棧里相比較,由于地址不同肯定是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
新聞熱點
疑難解答