Why does comparing strings using either '==' or 'is' sometimes produce a different result?
我有一个python程序,其中两个变量被设置为值
现在,如果我打开我的python解释器并进行相同的"is"比较,它就会成功。
1 2 3 4 | >>> s1 = 'public' >>> s2 = 'public' >>> s2 is s1 True |
我这里缺什么?
1 2 3 4 5 6 | >>> a = 'pub' >>> b = ''.join(['p', 'u', 'b']) >>> a == b True >>> a is b False |
难怪它们不一样,对吧?
换言之,
这里的其他答案是正确的:
Interned strings speed up string
comparisons, which are sometimes a
performance bottleneck in applications
(such as compilers and dynamic
programming language runtimes) that
rely heavily on hash tables with
string keys. Without interning,
checking that two different strings
are equal involves examining every
character of both strings. This is
slow for several reasons: it is
inherently O(n) in the length of the
strings; it typically requires reads
from several regions of memory, which
take time; and the reads fills up the
processor cache, meaning there is less
cache available for other needs. With
interned strings, a simple object
identity test suffices after the
original intern operation; this is
typically implemented as a pointer
equality test, normally just a single
machine instruction with no memory
reference at all.
因此,当程序中有两个字符串文本(字面上键入程序源代码的单词,用引号括起来)具有相同的值时,python编译器将自动对字符串进行实习生,使它们都存储在相同的内存位置。(请注意,这并不总是发生的,发生这种情况时的规则非常复杂,因此请不要在生产代码中依赖这种行为!)
因为在交互会话中,两个字符串实际上存储在同一个内存位置,它们具有相同的标识,所以
如果使用
最后要注意的一点是,您可以使用intern函数来确保获得对同一字符串的引用:
1 2 3 4 | >>> a = intern('a') >>> a2 = intern('a') >>> a is a2 True |
正如上面指出的,您可能不应该做的是确定字符串的相等性。但这可能有助于了解您是否有某种奇怪的需求来使用
注意,对于python 3,intern函数已经从内置函数转移到了模块
假设你有一个简单的
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Person(object): def __init__(self, name, age): self.name = name self.age = age def __eq__(self, other): return self.name == other.name and self.age == other.age jack1 = Person('Jack', 23) jack2 = Person('Jack', 23) jack1 == jack2 #True jack1 is jack2 #False |
他们同龄,但他们不是同一个人。一个字符串可能等价于另一个,但它不是同一个对象。
这是一个旁注,但在惯用的python中,您经常会看到如下内容:
1 2 | if x is None: # some clauses |
这是安全的,因为保证有空对象的一个实例(即无)。
如果您不确定要做什么,请使用"=="。如果你对它有更多的了解,你可以用"is"来表示"none"这样的已知对象。
否则,你最终会想,为什么事情不起作用,为什么会发生这种情况:
1 2 3 4 5 6 7 8 | >>> a = 1 >>> b = 1 >>> b is a True >>> a = 6000 >>> b = 6000 >>> b is a False |
我甚至不确定是否保证在不同的Python版本/实现之间保持不变。
从我对python有限的经验来看,
下面是一个很好的例子:
1 2 3 4 5 6 | >>> s1 = u'public' >>> s2 = 'public' >>> s1 is s2 False >>> s1 == s2 True |
我认为这与这样一个事实有关:当"is"比较结果为false时,将使用两个不同的对象。如果它的计算结果为真,这意味着它在内部使用的是同一个精确的对象,而不是创建一个新的对象,可能是因为您在2秒左右的时间内创建了它们,并且因为在优化和使用同一个对象之间没有很大的时间间隔。
这就是为什么应该使用相等运算符
1 2 3 4 5 6 7 8 9 10 | >>> s = 'one' >>> s2 = 'two' >>> s is s2 False >>> s2 = s2.replace('two', 'one') >>> s2 'one' >>> s2 is s False >>> |
在这个例子中,我创建了s2,它是一个不同的字符串对象,以前等于"one",但它与
我相信这就是所谓的"实习"弦。Python这样做,Java也是如此,C和C++也是这样编译的。
如果使用两个相同的字符串,而不是通过创建两个字符串对象来浪费内存,则具有相同内容的所有内部字符串都指向相同的内存。
这导致python"is"运算符返回true,因为两个内容相同的字符串指向同一个字符串对象。这也将发生在爪哇和C.。
不过,这只对节省内存有用。您不能依赖它来测试字符串是否相等,因为各种解释程序、编译器和JIT引擎不能总是这样做。
我正在回答这个问题,尽管这个问题已经过时了,因为上面没有引用语言参考的答案。
实际上,is运算符检查标识,==运算符检查相等性,
从语言引用:
类型几乎影响对象行为的所有方面。甚至对象标识的重要性在某种意义上也会受到影响:对于不可变类型,计算新值的操作实际上可能返回对具有相同类型和值的任何现有对象的引用,而对于可变对象,则不允许这样做。例如,在a=1;b=1之后,a和b可能或可能不引用值为1的同一对象,具体取决于实现,但在c=[]之后;d=[],c和d保证引用两个不同的、唯一的、新创建的空列表。(注意c=d=[]将相同的对象同时分配给c和d。)
因此,从上面的语句我们可以推断,当用"is"检查时,不可变类型的字符串可能会失败,而当用"is"检查时,可能会成功。
同样适用于int和tuple,它们也是不可变的类型。
1 2 3 4 | >>> a = 'banana' >>> b = 'banana' >>> a is b True |
在本例中,python只创建了一个字符串对象,
1 2 3 4 | >>> a = 'a longer banana' >>> b = 'a longer banana' >>> a == b, a is b (True, False) |
创建两个列表时,会得到两个对象:
1 2 3 4 | >>> a = [1, 2, 3] >>> b = [1, 2, 3] >>> a is b False |
在这种情况下,我们会说这两个列表是等效的,因为它们具有相同的元素,但不相同,因为它们不是同一个对象。如果两个对象是相同的,那么它们也是等效的,但是如果它们是等效的,那么它们就不一定是相同的。
如果
1 2 3 4 | >>> a = [1, 2, 3] >>> b = a >>> b is a True |
在大多数情况下,如果是
1 2 3 4 5 | >>> nan = float('nan') >>> nan is nan True >>> nan == nan False |
所以,您只能使用