2012-07-13 56 views
127

可能重複:
Python 「is」 operator behaves unexpectedly with integers爲什麼(0-6)是-6 =假?

今天我試圖調試我的項目,我會得到這個分析的幾個小時後:

>>> (0-6) is -6 
False 

但是,

>>> (0-5) is -5 
True 

你能向我解釋一下,爲什麼? 也許這是某種錯誤或非常奇怪的行爲。

> Python 2.7.3 (default, Apr 24 2012, 00:00:54) [GCC 4.7.0 20120414 (prerelease)] on linux2 
>>> type(0-6) 
<type 'int'> 
>>> type(-6) 
<type 'int'> 
>>> type((0-6) is -6) 
<type 'bool'> 
>>> 
+6

這就是徹頭徹尾的古怪 – Wug 2012-07-13 18:30:46

+25

什麼導致你首先使用'is'?除了'is/is not None'外,這不是Python中經常使用的東西。 – 2012-07-13 18:35:03

+3

@拉塞爾的評論觸及頭部 - 問題在於有人顯然使用「是」來比較數字,並期望它像'='一樣運作,這是一個不正確的期望。 – LarsH 2012-07-13 20:11:34

回答

150

所有整數從-5到256包緩存爲全局對象與CPython的共享相同的地址,因此is測試通過。

這個神器在http://www.laurentluce.com/posts/python-integer-objects-implementation/中詳細解釋,我們可以在http://hg.python.org/cpython/file/tip/Objects/longobject.c中查看當前源代碼。

一個特定的結構用於引用小整數並共享它們,因此訪問速度很快。它是一個262個指向整數對象的指針數組。這些整型對象在我們上面看到的整數對象塊的初始化過程中分配。小整數的範圍是從-5到257.許多Python程序花費了大量的時間使用整數範圍,所以這是一個明智的決定。

這只是CPython的一個實現細節,你不應該依賴這個。例如,PyPy實現了整數的id以返回自身,所以(0-6) is -6始終爲真,即使它們在內部是「不同的對象」。它還允許您配置是否啓用此整數緩存,甚至設置下限和上限。但總的來說,從不同來源檢索的對象不會完全相同。如果你想比較平等,只需使用==

+2

這就是我要說的,但我無法正確地說出來。 +1 – mpen 2012-07-13 18:30:19

+1

有趣的歪斜的積極面。文章說「許多Python程序花費了大量的時間在整個範圍內使用整數」,所以開發者可能已經以某種方式測量了它。我猜負數字文字只用於錯誤代碼這幾天... – 2012-07-13 18:34:30

+0

謝謝你@KennyTM。 – 2012-07-13 18:47:09

26

這不是一個錯誤。 is不是平等測試。 ==將會給出預期的結果。

此行爲的技術原因是Python實現可以自由地將同一個常量值的不同實例視爲同一個對象或不同的對象。您正在使用的Python實現選擇使某些小常量共享相同的對象,以節省內存。你不能依賴這種行爲是版本相同的版本,或者跨越不同的Python實現。

+2

>「是」不是平等測試。這個。 'is'是一個身份測試,看看兩個物體是否完全相同。恰巧在CPython實現中,一些int對象被緩存。 – Darthfett 2012-07-13 18:41:00

+1

+1對於沒有Python的開發者來說,這對我來說最好。 – 2012-07-14 11:18:07

29

Python的門店範圍內的整數-5 - 256的解釋:它從返回這些整數整數對象池。這就是爲什麼這些對象是相同的:(0-5)-5但不是(0-6)-6,因爲它們是在現場創建的。

下面是CPython的源代碼的源代碼:

#define NSMALLPOSINTS   257 
#define NSMALLNEGINTS   5 
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS]; 

view CPython source code/trunk/Objects/intobject.c)。源代碼包括以下注釋:

/* References to small integers are saved in this array so that they 
    can be shared. 
    The integers that are saved are those in the range 
    -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive). 
*/ 

is操作者然後將它們進行比較(-5)爲相等的,因爲它們是相同的對象(同一存儲器位置),但是另兩個新整數(-6)將在不同的內存位置(然後is不會返回True)。請注意,上述源代碼中的257用於正整數,因此是0 - 256(含)。

source

16

它發生,因爲CPython的緩存一些小的整數和小弦,並給出了對象相同id()的每個實例。

(0-5)-5id()相同的值,這是不正確的0-6-6

>>> id((0-6)) 
12064324 
>>> id((-6)) 
12064276 
>>> id((0-5)) 
10022392 
>>> id((-5)) 
10022392 

同樣,對於字符串:

>>> x = 'abc' 
>>> y = 'abc' 
>>> x is y 
True 
>>> x = 'a little big string' 
>>> y = 'a little big string' 
>>> x is y 
False 

有關字符串緩存的更多信息,請閱讀:is operator behaves differently when comparing strings with spaces

+2

那麼爲什麼'-6'不考慮「大」和「-5」?什麼是符合標準的東西被認爲是「大」? – inspectorG4dget 2012-07-13 18:36:52

+1

對於CPython,-5到256是「interned」(緩存)。這是一個有點武斷的實現選擇。如果一個給定的被攔截的對象被大量使用,那麼可能會節省大量的內存,但是在運行時或內存中實施它是有成本的,所以你不想爲所有事情做這件事。 – 2012-07-13 18:40:15

+0

用於顯示ID;我剛剛將這一點添加到我的答案中。 – 2012-07-13 18:43:56