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

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

提问人:jottos 提问时间:10/1/2009 最后编辑:Mateen Ulhaqjottos 更新时间:1/26/2023 访问量:1650711

问:

两个字符串变量设置为相同的值。 总是返回 ,但有时返回 。s1 == s2Trues1 is s2False

如果我打开我的 Python 解释器并进行相同的比较,它会成功:is

>>> s1 = 'text'
>>> s2 = 'text'
>>> s1 is s2
True

为什么会这样?

python 字符串 比较 身份 平等

评论

9赞 Nick Dandoulakis 10/1/2009
请参阅:stackoverflow.com/questions/1392433/...
3赞 Semjon Mössinger 8/21/2016
当您通过以下方式读取控制台输入时,也会出现此问题: .在本例中,输入为“y”,将返回“True”,而将返回 False。input = raw_input("Decide (y/n): ")if input == 'y':if input is 'y':
4赞 Chris_Rands 10/5/2016
这篇博客提供了比任何答案都更完整的解释 guilload.com/python-string-interning
1赞 ThorSummoner 10/6/2016
正如 @chris-rico 提到的,我在这里做了很好的解释 stackoverflow.com/q/15541404/1695680
4赞 Taknok 8/15/2017
Python 中的“==”和“is”之间有区别吗?

答:

122赞 Thomas Owens 10/1/2009 #1

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

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

22赞 meder omuraliev 10/1/2009 #2

我认为这与以下事实有关:当“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',但它与 不是同一个对象,因为解释器没有使用相同的对象,因为我最初没有将其分配给 'one',如果我有它,它们会成为同一个对象。s

评论

5赞 Daniel Pryden 10/2/2009
但是,在这种情况下用作示例可能不是最好的,因为它的语义可能会令人困惑。 将始终创建一个新的 String 对象,将新的 String 对象分配给 ,然后释放过去指向的 String 对象。因此,即使你这样做了,你仍然会得到一个新的字符串对象。.replace()s2 = s2.replace()s2s2s = s.replace('one', 'one')
1673赞 SilentGhost 10/1/2009 #3

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

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

所以,难怪它们不一样,对吧?

换言之:等价于a is bid(a) == id(b)

评论

19赞 jottos 10/2/2009
啊,和情商一样?vs 相等?在计划中,明白了。
57赞 Tyler 2/4/2012
或者 Java 中的 vs。最好的部分是 Python 与 Java 不同。==.equals()====
12赞 SilentGhost 10/29/2012
@Крайст:只有一个值。所以它总是具有相同的 id。None
24赞 user2864740 9/10/2014
这并不能解决 OP 的“is -> True”示例。
8赞 Chris Rico 2/1/2015
@AlexanderSupertramp,因为字符串实习
26赞 Jack M. 10/1/2009 #4

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

下面是一个很好的例子:

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

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

评论

1赞 0 _ 4/17/2021
此结果是由于另一个原因造成的:将 unicode 字符串 () 与非 unicode 字符串 () 进行比较。它是特定于 Python 2 的行为。在 Python 3 中,和 的类型都为 ,并且 和 都返回 。<type 'unicode'><type 'str'>s1stris==True
17赞 Zan Lynx 10/1/2009 #5

我认为这被称为“interned”字符串。Python这样做,Java也是如此,在优化模式下编译时,C和C++也是如此。

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

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

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

评论

0赞 CumaTekin 12/26/2022
是的,这就是我所期望的答案。好的,Hari 是对的,但是 python 是如何做到这一点的。感谢 Zan 的内存优化。
0赞 Peter Mortensen 1/8/2023
和 .NET(C#VB.NET 等)?
661赞 Daniel Pryden 10/2/2009 #6

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

以交互方式工作的原因是(大多数)字符串文字默认是内部的。来自维基百科:is

被隔离的字符串加速字符串 比较,有时是 应用程序中的性能瓶颈 (例如编译器和动态 编程语言运行时),即 严重依赖哈希表 字符串键。无需实习, 检查两个不同的字符串 是平等的,涉及检查每一个 两个字符串的字符。这是 慢有几个原因:它是 固有的 O(n) 的长度 字符串;它通常需要读取 从几个内存区域,其中 花点时间;并且读取填满了 处理器缓存,意味着更少 缓存可用于其他需求。跟 interned strings,一个简单的对象 在 原实习生操作;这是 通常作为指针实现 相等性测试,通常只是一个 无记忆的机器指令 完全参考。

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

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

评论

8赞 Noctis Skytower 4/11/2013
在哪里可以读到更多关于字符串被拘禁的复杂规则的信息?
110赞 That1Guy 4/29/2013
+1 进行彻底的解释。不知道另一个答案是如何在不解释实际发生的事情的情况下获得如此多的赞成票的。
6赞 Sнаđошƒаӽ 7/19/2015
这正是我读到这个问题时想到的。公认的答案很简短,但包含事实,但这个答案更好地解释了事情。好!
5赞 xtreak 3/10/2016
@NoctisSkytower谷歌搜索了一下,发现这个 guilload.com/python-string-interning
6赞 Daniel Pryden 10/19/2016
@naught101:不,规则是在您想要的检查类型之间进行选择。如果您关心字符串是否相等(即具有相同的内容),那么您应该始终使用 .如果您关心任何两个 Python 名称是否引用同一个对象实例,则应使用 .如果你正在编写处理大量不同值而不关心其内容的代码,或者如果你知道只有某物之一,并且你想忽略其他伪装成该物的对象,则可能需要这样做。如果您不确定,请始终选择 .==is==isis==
65赞 Jason Baker 10/2/2009 #7

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

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

正如前面的答案所指出的,你不应该用来确定字符串的相等性。但这可能有助于了解您是否有某种奇怪的要求。isis

请注意,该函数曾经是 Python 2 上的内置函数,但它已移至 Python 3 中的模块。internsys

34赞 Mattias Nilsson 10/2/2009 #8

如果您不确定自己在做什么,请使用“==”。 如果你对它有更多的了解,你可以使用“is”来表示已知对象,如“None”。

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

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

我什至不确定某些东西是否保证在不同的 Python 版本/实现之间保持不变。

评论

1赞 Paul 12/23/2014
一个有趣的例子显示了重新赋值 ints makes 如何触发这种情况。为什么会失败?是因为实习还是其他原因?
0赞 Paul 12/23/2014
看起来 is 返回 false 的原因可能是由于解释器实现:stackoverflow.com/questions/132988/...
0赞 Mattias Nilsson 12/29/2015
@ArchitJain 是的,这些链接很好地解释了这一点。当你阅读它们时,你就会知道你可以在哪些数字上使用“is”。我只是希望他们能解释为什么这样做仍然不是一个好主意:)您知道这一点并不意味着假设其他人也这样做(或者内部化的数字范围永远不会改变)不是一个好主意
40赞 Gregg Lind 10/2/2009 #9

这是一个旁注,但在惯用的 Python 中,您经常会看到以下内容:

if x is None:
    # Some clauses

这是安全的,因为保证有一个 Null 对象实例(即 None)。

评论

1赞 HandyManDan 6/2/2018
True 和 False 是一样的吗?只有一个实例,所以会匹配吗?
1赞 kamillitw 11/16/2018
@HandyManDan 是的,它们在 python 2 和 3 中都是单例。
1赞 Martijn Pieters 3/10/2019
@kamillitw但在 Python 2 中,您可以重新分配 False 和 True。
49赞 TankorSmash 4/29/2013 #10

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

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

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

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

评论

1赞 Flimm 2/10/2020
如果你更改了 set ,那不会改变。那是因为它们是两个不同的实例,所以.但是,如果他们的名字和年龄相同,他们可以相互平等。对于字符串来说,它变得更加复杂,因为字符串在 Python 中是不可变的,而 Python 经常重用相同的实例。我喜欢这个解释,因为它使用简单情况(普通对象)而不是特殊情况(字符串)。jack1.age = 99jack2.agejack1 is not jack2jack1 == jack2
14赞 Ram 8/9/2014 #11

实际上,运算符检查标识,== 运算符检查是否相等。is

从语言参考:

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

因此,从上面的语句中我们可以推断出,字符串是不可变类型,当用“is”检查时可能会失败,而当用“is”检查时可能会成功。

这同样适用于 和 它们也是不可变的类型。inttuple

2赞 Ryan 4/24/2017 #12

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

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

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

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

18赞 X. Wang 7/5/2017 #13

运算符测试值等效性。运算符测试对象身份,Python 测试两者是否真的是同一个对象(即,位于内存中的同一地址)。==is

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

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

>>> 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

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

如果引用一个对象并且您分配 ,则两个变量都引用同一个对象:ab = a

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

参考资料:Think Python 2e,作者:Allen B. Downey

评论

0赞 rossdavidh 10/4/2022
这是唯一能真正解释所见行为的答案,尤其是它有时以一种方式工作,有时以另一种方式工作的部分,具体取决于字符串长度。顺便说一句,取两个字符串的 lower() 也可以将结果从 True 切换到 False。
7赞 johnashu 9/15/2017 #14

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

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

is检查地址级别的等效性

==检查值级别等效性

-4赞 praveen kumar 12/27/2020 #15

在处理这个问题时,我们必须清楚的基本概念是理解 is== 之间的区别。

“is”是将比较内存位置。如果 id(a)==id(b),则 a 是 b 返回 true,否则返回 false。

因此,我们可以说这是用于比较内存位置的。而

==用于相等性测试,这意味着它只比较结果值。下面显示的代码可以作为上述给定理论的示例。

法典

Click here for the code

对于字符串文字(未分配给变量的字符串),内存地址将与图中所示相同。所以,id(a)==id(b)。剩下的就是不言自明的。

评论

1赞 flaxel 12/27/2020
你能直接在代码标签中发布你的代码吗?
0赞 Peter Mortensen 1/26/2023
请查看为什么在提问时不上传代码/错误的图像?(例如,“图像只能用于说明无法通过任何其他方式阐明的问题,例如提供用户界面的屏幕截图。提前致谢。