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

jot*_*tos 1076 python string comparison identity equality

我有一个Python程序,其中两个变量设置为该值'public'.在条件表达式中,我的比较var1 is var2失败了,但是如果我将其更改为var1 == var2它则返回True.

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

>>> s1 = 'public'
>>> s2 = 'public'
>>> s2 is s1
True
Run Code Online (Sandbox Code Playgroud)

我在这里错过了什么?

Sil*_*ost 1452

is是身份测试,==是平等测试.您的代码中会发生什么,将在解释器中模拟如下:

>>> a = 'pub'
>>> b = ''.join(['p', 'u', 'b'])
>>> a == b
True
>>> a is b
False
Run Code Online (Sandbox Code Playgroud)

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

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

  • 或者Java中的`==`vs` .equals()`.最好的部分是Python` ==`与Java` ==`不相似. (43认同)
  • 和eq一样吗?vs相等?在计划中,得到它. (17认同)
  • 这并没有解决OP的"是 - >真"的例子. (17认同)
  • @Крайст:只有一个'None`值.所以它始终具有相同的ID. (10认同)
  • @AlexanderSupertramp,因为[string interning](http://stackoverflow.com/questions/15541404/python-string-interning). (6认同)
  • Common Lisp中有很多等式谓词,除了eq,eql,equal和equalp的基本层次结构(名称越长,找到的东西就越多). (4认同)
  • 为什么"s2 s1"在问题中是真的呢? (4认同)
  • @pfabri:那是一篇*糟糕的*文章。它包括一个糟糕的建议“要遵循的经验法则是在比较不可变类型(如整数)时使用 ==”,在比较对象时使用“is”。” 您几乎*从不*想要使用“is”来比较用户定义的对象。正确的经验法则是“对*所有内容*使用“==”,但“None”和“NotImplemented”比较除外,直到您了解自己在做什么为止。” 该文章还未能传达出“int”的身份语义是一个 CPython 实现细节,仅适用于一小部分“int”,而不是所有“int”通用的某种缓存机制。 (3认同)

Dan*_*den 537

这里的其他答案是正确的:is用于身份比较,同时==用于相等比较.因为你关心的是相等(两个字符串应该包含相同的字符),在这种情况下,is操作符是完全错误的,你应该使用它==.

原因is工程交互是(最)字符串文字拘留默认.来自维基百科:

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

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

由于在交互式会话中,两个字符串实际上都存储在同一个内存位置,因此它们具有相同的标识,因此is运算符可以按预期工作.但是,如果你用一些其他的方法创建一个字符串(即使该字符串包含恰好相同的字符),则该字符串可能是相等的,但不是相同的字符串 -也就是说,它有不同的身份,因为它是存储在内存中的不同位置.

  • +1以获得详尽的解释.不知道其他答案如何收到这么多的赞成,而没有解释实际发生的事情. (78认同)
  • 哪些人可以阅读更多关于字符串实习的复杂规则? (6认同)
  • 这正是我读到这个问题时的想法.接受的答案很短但包含事实,但这个答案*解释*事情要好得多.太好了! (4认同)
  • @ naught101:不,规则是根据你想要的检查选择`==`和`is`.如果你关心字符串*等于*(即具有相同的内容),那么你应该总是使用`==`.如果你关心任何两个Python名称是否引用同一个对象实例,你应该使用`is`.如果您编写处理许多不同值的代码而不关心其内容,则可能需要`is`,否则如果您知道只有一个东西而您想忽略其他伪装成该东西的对象.如果你不确定,总是选择`==`. (4认同)
  • @NoctisSkytower用Google搜索相同的内容并找到了这个http://guilload.com/python-string-interning/ (3认同)

Tho*_*ens 101

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

如果使用is,当且仅当对象是同一对象时,结果才为真.但是,==只要对象的值相同,就会出现这种情况.


Jas*_*ker 56

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

>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True
Run Code Online (Sandbox Code Playgroud)

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

请注意,实习函数已从内置函数转移到sysPython 3 的模块中.


Tan*_*ash 42

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

假设你有一个简单的person对象.如果它被命名为'杰克'并且是'23'岁,它相当于另一个23岁的杰克,但它不是同一个人.

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
Run Code Online (Sandbox Code Playgroud)

他们的年龄相同,但他们不是同一个人.字符串可能与另一个字符串等效,但它不是同一个对象.


Gre*_*ind 36

这是一个旁注,但在惯用的python中,你经常会看到如下内容:

if x is None: 
    # some clauses
Run Code Online (Sandbox Code Playgroud)

这是安全的,因为可以保证Null对象的一个​​实例(即None).


Mat*_*son 27

如果您不确定自己在做什么,请使用'=='.如果您对此有更多的了解,可以使用'is'作为'None'等已知对象.

否则你最终会想知道为什么事情不起作用以及为什么会这样:

>>> a = 1
>>> b = 1
>>> b is a
True
>>> a = 6000
>>> b = 6000
>>> b is a
False
Run Code Online (Sandbox Code Playgroud)

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


Jac*_* M. 20

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

这是一个很好的例子:

>>> s1 = u'public'
>>> s2 = 'public'
>>> s1 is s2
False
>>> s1 == s2
True
Run Code Online (Sandbox Code Playgroud)

s1是一个unicode字符串,s2是一个普通的字符串.它们的类型不同,但值相同.


med*_*iev 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
>>> 
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我创建了s2,它是一个先前等于'one'的不同字符串对象,但它不是同一个对象s,因为解释器没有使用与我最初没有将它分配给'one'的相同对象,如果我拥有它会使它们成为同一个对象.

  • 但是,在此上下文中使用`.replace()`作为示例可能不是最好的,因为它的语义可能令人困惑.`s2 = s2.replace()`将*总是*创建一个*new*字符串对象,将新的字符串对象分配给`s2`,然后处理`s2`用于指向的字符串对象.所以,即使你做了`s = s.replace('one','one'),你仍然会得到一个新的字符串对象. (3认同)

Zan*_*ynx 13

我相信这被称为"实习"字符串.Python可以做到这一点,Java也是如此,在优化模式下进行编译时,C和C++也是如此.

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

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

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


Ram*_*Ram 11

我正在回答这个问题,即使这个问题已经过时了,因为上面的答案没有引用语言参考

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

来自语言参考:

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

所以从上面的语句我们可以推断,当用"is"检查时,不可变类型的字符串可能会失败,并且当用"is"检查时可能检查成功

这同样适用于int,tuple,它们也是不可变类型


X. *_*ang 8

==运营商测试值等价.该is运营商的测试对象标识,Python的测试两者是否真的是同一个对象(即住在内存中的地址相同).

>>> a = 'banana'
>>> b = 'banana'
>>> a is b 
True
Run Code Online (Sandbox Code Playgroud)

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

>>> a = 'a longer banana'
>>> b = 'a longer banana'
>>> a == b, a is b
(True, False)
Run Code Online (Sandbox Code Playgroud)

创建两个列表时,会得到两个对象:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False
Run Code Online (Sandbox Code Playgroud)

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

如果a引用一个对象而你指定了b = a,那么两个变量都引用同一个对象:

>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True
Run Code Online (Sandbox Code Playgroud)


joh*_*shu 7

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

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

is 检查地址级别的等效性

== 检查价值水平的等价性