首頁  >  文章  >  後端開發  >  Python 中的 is 和 id

Python 中的 is 和 id

高洛峰
高洛峰原創
2016-10-18 10:06:381131瀏覽

(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判斷。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn