关于python:为什么使用”==”或”is”比较字符串有时会产生不同的结果?

Why does comparing strings using either '==' or 'is' sometimes produce a different result?

我有一个python程序,其中两个变量被设置为值'public'。在条件表达式中,我比较了var1 is var2,但如果我把它改为var1 == var2,它会返回True

现在,如果我打开我的python解释器并进行相同的"is"比较,它就会成功。

1
2
3
4
>>> s1 = 'public'
>>> s2 = 'public'
>>> s2 is s1
True

我这里缺什么?


is是身份测试,==是平等测试。代码中发生的事情将在解释器中模拟,如下所示:

1
2
3
4
5
6
>>> a = 'pub'
>>> b = ''.join(['p', 'u', 'b'])
>>> a == b
True
>>> a is b
False

难怪它们不一样,对吧?

换言之,isid(a) == id(b)


这里的其他答案是正确的:is用于身份比较,==用于平等比较。因为您关心的是相等(两个字符串应该包含相同的字符),在这种情况下,is运算符是错误的,您应该改为使用==

is交互工作的原因是(大多数)字符串文本在默认情况下是内部的。维基百科:

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编译器将自动对字符串进行实习生,使它们都存储在相同的内存位置。(请注意,这并不总是发生的,发生这种情况时的规则非常复杂,因此请不要在生产代码中依赖这种行为!)

因为在交互会话中,两个字符串实际上存储在同一个内存位置,它们具有相同的标识,所以is操作符按预期工作。但是,如果您使用其他方法构造一个字符串(即使该字符串包含完全相同的字符),那么该字符串可能相等,但它不是同一个字符串——也就是说,它具有不同的标识,因为它存储在内存中的不同位置。


is关键字是对对象标识的测试,而==关键字是值比较。

如果使用is,则只有当对象是同一对象时,结果才会为真。但是,只要对象的值相同,==就会是真的。


最后要注意的一点是,您可以使用intern函数来确保获得对同一字符串的引用:

1
2
3
4
>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True

正如上面指出的,您可能不应该做的是确定字符串的相等性。但这可能有助于了解您是否有某种奇怪的需求来使用is

注意,对于python 3,intern函数已经从内置函数转移到了模块sys中。


is是身份测试,==是平等测试。这意味着,is是一种检查两个事物是相同的还是等价的方法。

假设你有一个简单的person物体。如果它的名字是"杰克",并且是"23岁",它相当于另一个23岁的杰克,但它不是同一个人。

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有限的经验来看,is用于比较两个对象,看它们是否是同一对象,而不是具有相同值的两个不同对象。==用于确定值是否相同。

下面是一个很好的例子:

1
2
3
4
5
6
>>> s1 = u'public'
>>> s2 = 'public'
>>> s1 is s2
False
>>> s1 == s2
True

s1是Unicode字符串,s2是普通字符串。它们不是同一类型,而是相同的值。


我认为这与这样一个事实有关:当"is"比较结果为false时,将使用两个不同的对象。如果它的计算结果为真,这意味着它在内部使用的是同一个精确的对象,而不是创建一个新的对象,可能是因为您在2秒左右的时间内创建了它们,并且因为在优化和使用同一个对象之间没有很大的时间间隔。

这就是为什么应该使用相等运算符==而不是is比较字符串对象的值。

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",但它与s不是同一个对象,因为解释器使用的对象与我最初没有将它赋给"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,它们也是不可变的类型。


==运算符测试值等效。is操作符测试对象标识,python测试这两个对象是否真的是同一个对象(即在内存中的同一个地址上活动)。

1
2
3
4
>>> a = 'banana'
>>> b = 'banana'
>>> a is b
True

在本例中,python只创建了一个字符串对象,ab都引用了它。原因是python内部缓存和重用一些字符串作为优化,实际上内存中只有一个字符串"b an an a",由a和b共享;要触发正常行为,需要使用更长的字符串:

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

在这种情况下,我们会说这两个列表是等效的,因为它们具有相同的元素,但不相同,因为它们不是同一个对象。如果两个对象是相同的,那么它们也是等效的,但是如果它们是等效的,那么它们就不一定是相同的。

如果a引用一个对象,而您分配b = a时,两个变量都引用同一个对象:

1
2
3
4
>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True

is是身份测试,==是平等测试(参见python文档)。

在大多数情况下,如果是a is b,则是a == b。但也有例外,例如:

1
2
3
4
5
>>> nan = float('nan')
>>> nan is nan
True
>>> nan == nan
False

所以,您只能使用is进行身份测试,而不能进行平等测试。


is将比较内存位置。用于对象级比较。

==将比较程序中的变量。它用于在值级别进行检查。

is检查地址级等效性

==检查值级等价性