2010-11-04 106 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