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


1146

我有一个Python程序,其中将两个变量设置为value 'public'。在条件表达式我有比较var1 is var2其失败,但如果我把它改为var1 == var2返回True

现在,如果我打开Python解释器并进行相同的“是”比较,则成功。

>>> s1 = 'public'
>>> s2 = 'public'
>>> s2 is s1
True

我在这里想念什么?



3
当你读一个控制台输入通过例如也会发生此问题:input = raw_input("Decide (y/n): ")。在这种情况下,输入“ y” if input == 'y':将返回“ True”,而if input is 'y':将返回False。
SemjonMössinger16年

4
这个博客提供了比任何答案都要
Chris_Rands

1
正如@ chris-rico所提到的,我在这里做了很好的解释stackoverflow.com/q/15541404/1695680
ThorSummoner 2016年

Answers:


1532

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

>>> a = 'pub'
>>> b = ''.join(['p', 'u', 'b'])
>>> a == b
True
>>> a is b
False

所以,难怪他们不一样吧?

换句话说:isid(a) == id(b)


17
啊和eq一样吗?与平等?在计划中,得到它。
jottos

47
在Java中还是==vs。.equals()最好的部分是Python ==与Java不相似==
MatrixFrog

11
@Крайст:只有一个None值。因此,它始终具有相同的ID。
SilentGhost 2012年

18
这没有解决OP的“是-> True”示例。
user2864740 2014年

6
@AlexanderSupertramp,因为字符串是 interning 。
克里斯·里科

569

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

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

插入的字符串可加快字符串比较的速度,这有时是严重依赖带有字符串键的哈希表的应用程序(例如编译器和动态编程语言运行时)的性能瓶颈。在不进行实习的情况下,检查两个不同的字符串是否相等涉及检查两个字符串的每个字符。这很慢,原因有几个:字符串的长度固有地为O(n);它通常需要从多个内存区域进行读取,这需要时间。并且读取将填满处理器缓存,这意味着可用于其他需求的缓存较少。对于插入的字符串,在原始的内部操作之后,一个简单的对象身份测试就足够了;这通常被实现为指针相等性测试,

因此,当程序中有两个具有相同值的字符串文字(在程序源代码中逐字键入的单词,并用引号引起来)时,Python编译器将自动内插字符串,使它们都存储在相同的位置内存位置。(请注意,这并不总是会发生,并且发生这种情况的规则非常复杂,因此请不要在生产代码中依赖此行为!)

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


6
有人可以在哪里阅读有关卷入字符串的复杂规则的更多信息?
Noctis Skytower

88
+1为详尽的解释。不知道其他答案如何收到如此多的赞扬而没有解释实际发生了什么。
That1Guy 2013年

4
这正是我阅读问题时所想到的。可接受的答案简短但包含事实,但是此答案可以更好地说明问题。真好!
Sнаđошƒаӽ

3
@NoctisSkytower在Google上进行了相同的搜索,发现此guilload.com/python-string-interning
xtreak

5
@ naught101:没有,规则之间作出选择==,并is在样检查你想要什么根据。如果您关心字符串是否相等(即内容相同),则应始终使用==。如果您关心是否有两个Python名称引用同一对象实例,则应使用isis如果您正在编写处理许多不同值而不关心它们的内容的代码,或者如果您知道其中只有一个,并且您想忽略其他假装成那个对象的代码,则可能需要。如果不确定,请始终选择==
丹尼尔·普赖登

108

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

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


57

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

>>> from sys import intern
>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True

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

请注意,该intern函数以前是Python 2的内置函数,但已移至sysPython 3 的模块中。


43

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

假设您有一个简单的person对象。如果它的名字叫“ Jack”并且是“ 23”岁,则相当于另一个23岁的Jack,但不是同一个人。

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

他们是同一年龄,但他们不是同一个人。一个字符串可能等效于另一个,但它不是同一对象。


如果更改设置jack1.age = 99,那不会改变jack2.age。那是因为它们是两个不同的实例,所以jack1 is not jack2。但是,jack1 == jack2如果他们的名字和年龄相同,他们可以彼此平等。对于字符串来说,它变得更加复杂,因为字符串在Python中是不可变的,并且Python经常重用同一实例。我喜欢这种解释,因为它使用简单的情况(普通对象)而不是特殊的情况(字符串)。
Flimm


28

如果不确定自己在做什么,请使用'=='。如果您对此有更多了解,可以对已知对象(例如“无”)使用“ is”。

否则,您将最终想知道为什么事情不起作用以及为什么会发生这种情况:

>>> a = 1
>>> b = 1
>>> b is a
True
>>> a = 6000
>>> b = 6000
>>> b is a
False

我什至不确定在不同的python版本/实现之间是否可以保证某些事情保持不变。


1
有趣的示例显示了如何重新分配整数使此条件触发。为什么这失败了?是由于实习还是其他原因?
保罗

看起来is返回false的原因可能是由于解释器的实现:stackoverflow.com/questions/132988/…–
Paul


@ArchitJain是的,这些链接对其进行了很好的解释。阅读它们时,您会知道可以使用“是”的数字。我只是希望他们能解释为什么这样做仍然不是一个好主意:)您知道这并不意味着假设其他所有人也都做得好(否则内部数字范围将永远不会改变)
Mattias Nilsson

20

根据我在python中的有限经验,is用于比较两个对象以查看它们是否是同一对象,而不是两个具有相同值的不同对象。 ==用于确定值是否相同。

这是一个很好的例子:

>>> s1 = u'public'
>>> s2 = 'public'
>>> s1 is s2
False
>>> s1 == s2
True

s1是unicode字符串,并且s2是普通字符串。它们不是同一类型,但是具有相同的值。


17

我认为这与以下事实有关:当“ is”比较结果为false时,将使用两个不同的对象。如果评估结果为true,则表示内部使用的是完全相同的对象,而不是创建一个新对象,这可能是因为您在不到2秒的时间内创建了它们,并且在优化和使用相同的对象。

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

>>> s = 'one'
>>> s2 = 'two'
>>> s is s2
False
>>> s2 = s2.replace('two', 'one')
>>> s2
'one'
>>> s2 is s
False
>>> 

在此示例中,我创建了s2,它是一个以前等于'one'的不同字符串对象,但它与并不相同s,因为解释器没有使用相同的对象,因为我最初并未将其分配给'one',如果我有的话,会让他们成为同一个对象。


3
.replace()但是,在这种情况下使用示例可能不是最好的,因为其语义可能会令人困惑。 s2 = s2.replace()始终创建一个新的字符串对象,将新的字符串对象分配给s2,然后处置s2以前指向的字符串对象。因此,即使您这样做了,s = s.replace('one', 'one')您仍然会得到一个新的字符串对象。
丹尼尔·普里登

13

我相信这被称为“ interned”字符串。在优化模式下,Python会这样做,Java也会这样做,C和C ++也会这样做。

如果您使用两个相同的字符串,而不是通过创建两个字符串对象来浪费内存,则具有相同内容的所有已嵌入字符串都指向相同的内存。

这导致Python“ is”运算符返回True,因为两个内容相同的字符串指向同一个字符串对象。这也将在Java和C语言中发生。

但是,这仅对节省内存有用。您不能依靠它来测试字符串是否相等,因为各种解释器和编译器以及JIT引擎不能总是这样做。


12

我回答了这个问题,尽管这个问题已经很老了,因为上面没有答案引用了语言参考

实际上,is运算符检查身份,而==运算符检查是否相等,

从语言参考:

类型影响对象行为的几乎所有方面。甚至对象身份的重要性在某种意义上也受到影响:对于不可变类型,计算新值的操作实际上可能返回对具有相同类型和值的任何现有对象的引用,而对于可变对象,则不允许这样做。例如,在a = 1之后;b = 1,取决于实现,a和b可以或可以不使用值1引用同一对象,但是在c = []之后;d = [],保证c和d引用两个不同的,唯一的,新创建的空列表。(请注意,c = d = []将相同的对象分配给c和d。)

因此,根据上述陈述,我们可以推断出,使用“ is”检查时,不可变类型的字符串可能会失败,而使用“ is”检查时,则可能会检查成功

同样适用于int,tuple也是不可变的类型


8

==运营商测试值等价。该is运营商的测试对象的身份,Python的测试是否两者实际上是同一个对象(即住在内存中的地址相同)。

>>> a = 'banana'
>>> b = 'banana'
>>> a is b 
True

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

>>> a = 'a longer banana'
>>> b = 'a longer banana'
>>> a == b, a is b
(True, False)

创建两个列表时,将获得两个对象:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False

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

如果a引用对象,则分配b = a,然后,则两个变量都引用同一个对象:

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

7

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

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

is 检查地址级别是否相等

== 检查价值水平是否相等


3

is是身份测试,==是相等性测试(请参阅Python文档)。

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

>>> nan = float('nan')
>>> nan is nan
True
>>> nan == nan
False

因此,您只能is用于身份测试,而不能用于相等性测试。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.