2010-11-04 103 views
6

有人能解釋我在python 2.6.6上這個奇怪的結果嗎?Python中的奇怪結果

>>> a = "xx" 
>>> b = "xx" 
>>> a.__hash__() == b.__hash__() 
True 
>>> a is b 
True # ok.. was just to be sure 

>>> a = "x" * 2 
>>> b = "x" * 2 
>>> a.__hash__() == b.__hash__() 
True 
>>> a is b 
True # yeah.. looks ok so far ! 

>>> n = 2 
>>> a = "x" * n 
>>> b = "x" * n 
>>> a.__hash__() == b.__hash__() 
True # still okay.. 
>>> a is b 
False # hey! What the F... ? 
+5

人們知道'is'是什麼,但不知道它與'=='有什麼不同? – delnan 2010-11-04 15:56:38

+0

[Python'=='vs'可能重複'比較字符串','有時會失敗,爲什麼?](http://stackoverflow.com/questions/1504717/python-vs-is-comparing-strings-is -fails-sometimes-why) – SilentGhost 2010-11-04 16:02:50

+0

@SilentGhost:不完全是,因爲這涉及編譯器意外插入字符串時的主題。 – 2010-11-04 16:06:52

回答

12

要理解這一點,您需要了解一些不同的事情。

  • a is b返回true,如果ab相同的對象,而不僅僅是他們是否有相同的值。字符串可以具有相同的值,但是可以是該值的不同實例。
  • 當你說a = "x",你實際上在做的是創建一個字符串常量"x",然後給它分配一個名字,a。字符串常量是字面上寫在代碼中的字符串,不是以編程方式計算的。字符串常量始終爲,實際上是,這意味着它們存儲在表中以供重用:如果您說a = "a"; b = "a",它實際上與a = "a"; b = a相同,因爲它們將使用相同的字符串"a"。這就是爲什麼第一個a is b是真的。
  • 當你說a = "x" * 2時,Python編譯器實際上正在對此進行優化。它會在編譯時計算字符串 - 它會生成代碼,就好像您編寫的a = "xx"。因此,產生的字符串"xx'被執行。這就是爲什麼第二個a is b是真實的。
  • 當您說a = "x" * n時,Python編譯器在編譯時不知道n是。因此,它被迫實際輸出字符串"x",然後在運行時執行字符串乘法。由於這是在運行時執行的,而"x"被攔截,因此得到的字符串"xx"而不是。因此,這些字符串中的每一個都是"xx"的不同實例,因此最終的a is b是False。

你可以看到區別自己:

def a1(): 
    a = "x" 
def a2(): 
    a = "x" * 2 
def a3(): 
    n = 2 
    a = "x" * n 


import dis 
print "a1:" 
dis.dis(a1) 

print "a2:" 
dis.dis(a2) 

print "a3:" 
dis.dis(a3) 

在CPython的2.6.4,這個輸出:

a1: 
    4   0 LOAD_CONST    1 ('x') 
       3 STORE_FAST    0 (a) 
       6 LOAD_CONST    0 (None) 
       9 RETURN_VALUE 
a2: 
    6   0 LOAD_CONST    3 ('xx') 
       3 STORE_FAST    0 (a) 
       6 LOAD_CONST    0 (None) 
       9 RETURN_VALUE 
a3: 
    8   0 LOAD_CONST    1 (2) 
       3 STORE_FAST    0 (n) 

    9   6 LOAD_CONST    2 ('x') 
       9 LOAD_FAST    0 (n) 
      12 BINARY_MULTIPLY 
      13 STORE_FAST    1 (a) 
      16 LOAD_CONST    0 (None) 
      19 RETURN_VALUE 

最後要注意,你可以說a = intern(a); b = intern(b)創造實習的版本,如果字符串,這將保證a is b是真實的。但是,如果您只想檢查字符串相等性,只需使用a == b即可。

+0

Upvoted爲純粹的全面性 – kindall 2010-11-04 18:08:54

+0

關於實習生,我發現字符串低於或等於20個字符長度我真正的問題是,爲什麼「x」* 2(interned)不同於「x」* n ..(即使n <= 20也沒有被攔截) – pyrou 2010-11-05 08:58:12

17

is運營商告訴你兩個變量是否指向同一個對象在內存。它很少有用,並且經常與==操作符相混淆,它會告訴您兩個對象是否「看起來相同」。

當使用諸如短字符串之類的東西時,它尤其令人困惑,因爲Python編譯器會爲了提高效率而實施這些操作。換句話說,編寫"xx"時,編譯器(發出字節碼)在內存中創建一個字符串對象,並使所有文字"xx"指向它。這解釋了爲什麼你的前兩個比較是真的。請注意,您可以通過調用id對他們來說,這(至少在CPython的可能),它們在內存中的地址得到字符串的ID:

>>> a = "xx" 
>>> b = "xx" 
>>> id(a) 
38646080 
>>> id(b) 
38646080 
>>> a is b 
True 
>>> a = "x"*10000 
>>> b = "x"*10000 
>>> id(a) 
38938560 
>>> id(b) 
38993504 
>>> a is b 
False 

第三是因爲編譯器沒有拘留字符串ab,無論出於何種原因(可能是因爲它不夠聰明才能注意到變量n被定義了一次,然後從未被修改過)。

你實際上可以強制Python實習字符串,好吧,asking it to。這會給你一些額外的性能提升,並可能有所幫助。這可能是沒用的。

道德:不要使用is字符串文字。或者int文字。或者任何你不認真的地方,真的。

+0

這將是值得添加一個解釋爲什麼前兩個'a是b'調用的工作。 – 2010-11-04 15:51:14

+0

如果您想進一步檢查,請爲這三種情況測試'id(a)'和'id(b)'的結果。無論出於何種原因,* – user470379 2010-11-04 15:54:07

+0

*。那麼原因很明顯,不是嗎?因爲編譯器不會爲這些長字符串生成字節碼。 – SilentGhost 2010-11-04 15:54:50