2012-04-04 20 views
12

爲什麼() is()爲true,但(0,) is (0,)爲假?Python「is」語句和元組

我以爲他們會是同一個對象。但是,我顯然錯過了一些東西。

+10

「is」表示它們是內存中的同一個對象。顯然,CPython只有一個空元組的副本,但是對於帶有內容的元組來說卻是新的。 – Dougal 2012-04-04 04:36:04

+0

看起來像他們可以在內存中使用相同的位置來查看相同的元組,因爲它們是不可變的。然而,他們爲什麼不這樣做可能是有原因的。 – rectangletangle 2012-04-04 04:44:23

+0

@RectangleTangle,你需要一種有效的方法來找到相同的元組。想想你會怎樣做 – 2012-04-04 04:53:52

回答

12

is測試以查看語句的兩邊是否共享相同的內存地址。它基本上id(a) == id(b)

>>> print id(()), id(()) 
30085168 30085168 
>>> print id((0,)), id((0,)) 
38560624 38676432 
>>> 

一個速記()發生相當頻繁,它是由Python解釋器一單(就像從0到255的整數,空字符串,空列表等)實際上處理。當將(0,)(0,)與解釋器進行比較時,它們實際上是內存中的不同變量。如果它們是可變的,你可以修改第一個,第二個不會改變,因此它們不是相同的(a is not b)。

+9

空的列表不是單身人士;他們是可變的,所以你**希望**每次出現'[]'創建一個新的列表!另外,要小心使用'id'作爲臨時對象;在我的系統中輸入'(id([]),id([]))'到REPL中給出'(48511432L,48511432L)',但這並不意味着它們是同一個對象;只是第一個列表在被'id'後被丟棄爲垃圾,然後第二個列表被分配到這個方便的空列表大小的內存空間,剛剛釋放。除非確保對象的存活時間長於'id'值,否則'id'值可能沒有意義。 – Ben 2012-04-04 06:17:55

+0

@Ben,將名單分配給名稱(這將通過比較持續)將消除潛在的問題,對嗎? – rectangletangle 2012-04-04 07:00:10

+0

@RectangleTangle完全正確。 – Ben 2012-04-04 07:09:41

5

is測試身份,而不是平等。這意味着Python只是比較一個對象所在的內存地址。基本上回答了這個問題:「我對同一個對象有兩個名字嗎?」

通常Python將每個元組寫入不同的內存位置,
interning大多隻會發生在字符串文字上。

3

正如Dougal在他的評論中所說的,is測試了你正在比較的兩件事是在內存中的同一個地方。對於諸如數字,字符串,布爾值和空元組之類的東西,Python默認會重用對象(內聯),因此is通常會產生與==相同的行爲。這也意味着通過比較內存指針而不是更復雜的數據類型(如字符串)可以獲得性能提升。

對於其他的東西,如你的情況(即使它們是不可改變的)的元組,或列表,甚至空列表[],Python會在不同的存儲位置創建一個新的對象,is將無法​​正常工作一樣方式爲==

如果您嘗試按值比較兩個元組,==將是更好的比較。

+1

讓Python創建多個版本相同的字符串和數字是相當容易的。 Python不承諾實習他們,它可能會選擇這樣做來節省內存,所以對於這些對象,「is」的行爲變得不可預知。因此,儘管使用'is'來比較字符串*可以更高效,但它會冒着向代碼引入微妙的錯誤的風險,除非您只在受控的地方使用它,因爲您知道程序的行爲無法處理此問題。通常不可變的對象應該總是與'=='而不是'is'進行比較。 – Ben 2012-04-04 06:05:39

+0

@Ben,不會'a是b還是a == b'也起作用?儘管如此,第二次比較可能會否定所獲得的表現。 – rectangletangle 2012-04-04 06:18:27

+1

@RectangleTangle取決於;如果'a'通常不是'b',那麼你大部分時間只是在浪費工作。使用多個Python級別的操作來保存一個簡短的C循環也聽起來不太可能是一個巨大的回報,除非比較大的字符串本身是很常見的。當我測試的是*語義時,我只是使用'=='*當我測試的是*語義*身份時,相等和'是'。理論上,如果我發現字符串比較是一個太慢的程序中的重要瓶頸,那麼我可以回過頭來替換其中的一些,但這從來沒有發生過。 – Ben 2012-04-04 06:23:53