本文實例講述了Python中的is和id用法。分享給大家供大家參考。具體分析如下:
(ob1 is ob2) 等價于 (id(ob1) == id(ob2))
首先id函數可以獲得對象的內存地址,如果兩個對象的內存地址是一樣的,那么這兩個對象肯定是一個對象。和is是等價的。Python源代碼為證。
?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;
但是請看下邊代碼的這種情況怎么會出現呢?
...:???? 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.bar
Out[4]: False
?
In [5]: id(foo.bar) == id(Foo.bar)
Out[5]: True
兩個對象用is判斷是False,用id判斷卻是True,這與我們已知的事實不符啊,這種現象該如何解釋呢?遇到這種情況最好的解決方法就是調用dis模塊去看下兩個比較語句到底做了什么。
????????? 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。根據文檔:
When an instance attribute is referenced that isn't a data attribute,
its class is searched. If the name denotes a valid class attribute
that is a function object, a method object is created by packing
(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是沒有意義的。
一個更明顯的例子是,
輸出的結果也是True
看 id 的文檔:
Return the “identity” 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-overlapping lifetimes may
have the same id() value.
CPython implementation detail: This is the address of the object in memory.
只有你能保證對象不會被銷毀的前提下,你才能用 id 來比較兩個對象。所以,如果你非要比的話,得這樣寫:
Fb = Foo.bar
print id(fb) == id(Fb)
即把兩個表達式的結果綁定到名字上,再來比是不是同一個對象,你才能得到正確的結果。
is表達式也是一樣的,你現在得到了正確的結果,完全是因為 CPython 現在的實現細節決定的。現在的is的實現,是左右兩邊的對象都計算出來,然后再比較這兩個對象的地址是否一樣。萬一哪天改成了,先算左邊,保存地址,把左邊釋放掉,再算右邊,再比較的話,你的is的結果可能就錯了。官方文檔里也提到了這個問題 。我認為正確的方法也是像id那樣,先把左右兩邊都計算下來,并顯式綁定到各自的名字上,然后再用is判斷。
希望本文所述對大家的Python程序設計有所幫助。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
