2010-01-13 29 views
10

可能重複:
Python 「is」 operator behaves unexpectedly with integersPython的身份:多重人格障礙,需要代碼縮水

我偶然發現了以下Python weirdity:

>>> two = 2 
>>> ii = 2 

>>> id(two) == id(ii) 
True 
>>> [id(i) for i in [42,42,42,42]] 
[10084276, 10084276, 10084276, 10084276] 

>>> help(id) 
Help on built-in function id in module __builtin__: 

id(...) 
    id(object) -> integer 

    Return the identity of an object. This is guaranteed to be unique among 
    simultaneously existing objects. (Hint: it's the object's memory address.) 
  1. 是每numbe一個獨特的對象?
  2. 不同的變量是否保持相同的元素值(例如,兩個,ii)是同一個對象?
  3. Python是如何生成一個數字的ID?
  4. 在上面的例子中,有兩個指針指向一個存儲單元,其值爲2?這將是非常奇怪的。

幫我解開身份危機。

一些更weirdities:

>>> a,b=id(0),id(1) 
>>> for i in range(2,1000): 
    a,b=b,id(i) 
    if abs(a-b) != 12: 
    print('%i:%i -> %i' % (i,a,b)) 

上面的代碼檢查,如果連續整數ID也連續,並打印出 異常:

77:10083868 -> 10085840 
159:10084868 -> 10086840 
241:10085868 -> 10087840 
257:10087660 -> 11689620 
258:11689620 -> 11689512 
259:11689512 -> 11689692 
260:11689692 -> 11689548 
261:11689548 -> 11689644 
262:11689644 -> 11689572 
263:11689572 -> 11689536 
264:11689536 -> 11689560 
265:11689560 -> 11689596 
266:11689596 -> 11689656 
267:11689656 -> 11689608 
268:11689608 -> 11689500 
331:11688756 -> 13807288 
413:13806316 -> 13814224 
495:13813252 -> 13815224 
577:13814252 -> 13816224 
659:13815252 -> 13817224 
741:13816252 -> 13818224 
823:13817252 -> 13819224 
905:13818252 -> 13820224 
987:13819252 -> 13821224 

注意,模式從413開始出現。也許這是由於每個新內存頁面開始時的一些巫術記帳。

+1

這有什麼關係?你問來幹什麼?這會導致什麼問題?我不明白這個問題。請澄清什麼是壞的。 – 2010-01-13 17:48:28

+3

@美國洛特他試圖加深他的理解,有什麼不好呢?有時候旅程比目的地更值錢。 – Simon 2010-01-13 17:54:12

+1

我不明白的問題。如果問題不透明,我無法幫助加深理解。 – 2010-01-13 17:59:09

回答

4

你的第四個問題,「在上面的例子中,是指向一個存儲單元的值爲2的兩個和兩個指針,這將是非常奇怪的」,這真的是理解整個事物的關鍵。

如果您熟悉像C這樣的語言,那麼Python「變量」實際上並不一樣。 AC變量聲明,如:

int j=1; 
int k=2; 
k += j; 

說,「編譯,預留的內存我兩個領域,在棧中,每個有足夠的空間來容納一個整數,並記住一個爲‘J’,另一個爲「 k',然後用值'1'填充j,用'2'填充k。在運行時,代碼表示「獲取k的整數內容,添加j的整數內容,並將結果存回k中。」

看似等效代碼在Python:

j = 1 
k = 2 
k += j 

說不同的東西:「巨蟒,查找被稱爲‘1’的對象,並創建一個名爲‘J’指向它的標籤查找。 ('2'),查找對象'j'指向('1'),並創建一個名爲'k'的標籤指向它。 ),並將「k」指向由對這兩者執行「添加」操作所產生的對象。

反彙編代碼(與DIS模塊)顯示了這個很好:

2   0 LOAD_CONST    1 (1) 
       3 STORE_FAST    0 (j) 

    3   6 LOAD_CONST    1 (2) 
       9 STORE_FAST    1 (k) 

    4   12 LOAD_FAST    1 (k) 
      15 LOAD_FAST    0 (j) 
      18 INPLACE_ADD 
      19 STORE_FAST    1 (k) 

所以,是的,Python的「變量」是標籤指向的對象,而不是容器,可以是充滿數據。

其他三個問題都是「Python何時從一段代碼創建一個新對象,以及它何時重用它已有的一個對象?」的所有變體。後者被稱爲「實習」;它發生在整數和字符串看起來(Python),就像它們可能是符號名稱一樣。

+1

「對象稱爲'2',並創建一個名爲'j'的標籤'應該是'1' – 2010-08-17 18:38:34

+0

謝謝,tolomea - 固定! – 2010-08-17 20:20:10

9

介於-1和255之間的整數(?)以及字符串文字被執行。源中的每個實例實際上都代表同一個對象。

在CPython中,id()的結果是PyObject的進程空間中的地址。

+1

這隻適用於CPython。甚至在那裏,沒有人保證即使在語法凍結期間也不會改變。 – jcdyer 2010-01-13 18:04:41

+0

是的。在舊版本中,它只能達到99個左右。 – 2010-01-13 19:12:00

2

你應該對這些調查非常小心。您正在研究實施該語言的內部因素,而這些並不能保證。 id的幫助是專注於:對於兩個不同的對象,數字會有所不同,對於同一個對象,這個數字也是相同的。作爲一個實現細節,在CPython中它是對象的內存地址。 CPython可能決定隨時更改這些細節。

被整理到相同分配時間的小整數的細節也是一個可以隨時改變的細節。

此外,如果您從CPython切換到Jython或PyPy或IronPython,則除id()以外的所有投注都將關閉。

1

不是每個數字都是一個獨特的對象,事實上有些是CPython解釋器的優化細節。 不要依靠這種行爲。對於這個問題,千萬不要用is來測試是否相等。只有使用is,如果你確定你需要完全相同的對象。

+2

警告:比較'None'時,應始終使用'is'。 – jcdyer 2010-01-13 18:03:41

+0

和'省略號'。儘管你永遠不會這樣做。 – 2010-01-13 18:04:48

+3

直接從PEP-8中得出的一般規則是「用於比較單個對象時」。 這意味着無,真,假,省略等。然而,真和假一般最好完全省略,以允許發生布爾強制。 – 2010-01-13 18:08:57

8

的Python每個實現完全允許優化在任何程度上(包括....根本沒有;-)的身份和不可改變對象(如數字,元組和串分配)[[沒有這樣的緯度爲mutable對象,如列表,字典和集合]]。

這兩件不更改的對象引用ab之間,所有的實現必須保證的是:

  1. id(a) == id(b),AKA a is b,必須始終意味着a == b
  2. ,因此a != b必須始終意味着id(a) != id(b) AKA a is not b

特別注意有沒有約束,即使對於不可變類型,a == b必須暗示a is b(即,那id(a) == id(b))。只有None才能保證(所以你總是可以測試if x is None:而不是if x == None:)。

當前的CPython實現通過「合併」(具有單個分配,因此一個id)一定範圍內的小整數和內置的不可變類型對象的優點,不止一次給定函數中(所以舉例來說,如果你的函數f有四次出現的文字'foobar'他們都指的是函數的常量中的字符串'foobar'的一個實例,相較於將由存儲四個相同的允許實現節省一點空間但是這個常量的單獨副本)。

所有這些實現考慮的是相當次要的興趣到Python程序員的(除非你是一個Python實現,或者至少東西是緊密綁定到特定的實現,如調試系統工作)。