【问题标题】:Why does comparing strings using either '==' or 'is' sometimes produce a different result?为什么使用 '==' 或 'is' 比较字符串有时会产生不同的结果?
【发布时间】:2010-12-03 01:07:45
【问题描述】:

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

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

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

我在这里错过了什么?

【问题讨论】:

标签: python string comparison identity equality


【解决方案1】:

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

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

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

换句话说:a is b 等价于id(a) == id(b)

【讨论】:

  • 啊和eq一样吗?对等?在计划中,明白了。
  • == 与 Java 中的 .equals()。最好的部分是 Python == 与 Java == 不同。
  • @Крайст:只有一个 None 值。所以它总是有相同的 id。
  • 这并没有解决 OP 的“is -> True”示例。
  • @AlexanderSupertramp,因为string interning
【解决方案2】:

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

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

内部字符串加速字符串 比较,有时是 应用程序的性能瓶颈 (例如编译器和动态 编程语言运行时) 严重依赖哈希表 字符串键。没有实习, 检查两个不同的字符串 平等涉及检查每个 两个字符串的字符。这是 慢有几个原因:它是 本质上 O(n) 的长度 字符串;它通常需要读取 来自多个内存区域,其中 慢慢来;并且读数填满 处理器缓存,意味着更少 缓存可用于其他需求。和 实习字符串,一个简单的对象 身份测试后就足够了 原实习生操作;这是 通常实现为指针 平等测试,通常只有一个 没有记忆的机器指令 完全可以参考。

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

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

【讨论】:

  • 在哪里可以阅读更多关于何时插入字符串的复杂规则?
  • +1 以获得详尽的解释。在没有解释实际发生的情况的情况下,不确定其他答案如何获得如此多的支持。
  • 这正是我在阅读问题时所想到的。接受的答案很短但包含事实,但这个答案解释事情要好得多。不错!
  • @NoctisSkytower 用谷歌搜索了同样的东西,发现了这个guilload.com/python-string-interning
  • @naught101:不,规则是根据您想要的检查类型在==is 之间进行选择。如果您关心字符串是否相等(即具有相同的内容),那么您应该始终使用==。如果您关心任何两个 Python 名称是否引用同一个对象实例,您应该使用is。如果您正在编写处理许多不同值而不关心它们的内容的代码,或者如果您知道只有一个东西并且您想忽略其他假装是那个东西的对象,您可能需要is。如果您不确定,请始终选择==
【解决方案3】:

is 关键字是对象身份测试,而 == 是值比较。

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

【讨论】:

    【解决方案4】:

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

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

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

    请注意,intern 函数曾经是 Python 2 的内置函数,但在 Python 3 中已移至 sys 模块。

    【讨论】:

      【解决方案5】:

      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
      

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

      【讨论】:

      • 如果您更改集合jack1.age = 99,则不会更改jack2.age。那是因为它们是两个不同的实例,所以jack1 is not jack2。但是,如果他们的姓名和年龄相同,他们可以彼此相等jack1 == jack2。字符串变得更加复杂,因为字符串在 Python 中是不可变的,而且 Python 经常重用相同的实例。我喜欢这种解释,因为它使用简单的情况(普通对象)而不是特殊情况(字符串)。
      【解决方案6】:

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

      if x is None:
          # Some clauses
      

      这是安全的,因为there is guaranteed to be one instance of the Null Object (i.e., None)

      【讨论】:

      • True 和 False 一样吗?那么只有一个实例会匹配吗?
      • @HandyManDan 是的,它们在 python 2 和 3 中都是单例。
      • @kamillitw 但在 Python 2 中,您可以重新分配 False 和 True。
      【解决方案7】:

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

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

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

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

      【讨论】:

      • 有趣的例子展示了重新分配整数如何触发这种情况。为什么会失败?是实习还是其他原因?
      • 看起来 is 返回 false 的原因可能是由于解释器实现:stackoverflow.com/questions/132988/…
      • @ArchitJain 是的,这些链接解释得很好。当您阅读它们时,您会知道可以在哪些数字上使用“是”。我只是希望他们能解释为什么这样做仍然不是一个好主意 :) 你知道这并不意味着假设其他人也这样做是一个好主意(或者内部化的数字范围永远不会改变)
      【解决方案8】:

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

      这是一个很好的例子:

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

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

      【讨论】:

      • 这个结果是由不同的原因造成的:将 unicode 字符串 (<type 'unicode'>) 与非 unicode 字符串 (<type 'str'>) 进行比较。这是特定于 Python 2 的行为。在 Python 3 中,s1str 类型都属于,is== 都返回 True
      【解决方案9】:

      我认为这与以下事实有关,即当“is”比较评估为 false 时,使用了两个不同的对象。如果它评估为真,这意味着它在内部使用相同的确切对象而不是创建新对象,可能是因为您在 2 秒左右的时间内创建了它们,并且因为在优化和优化之间没有很大的时间间隔使用相同的对象。

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

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

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

      【讨论】:

      • 在这种情况下使用.replace() 作为示例可能不是最好的,因为它的语义可能会令人困惑。 s2 = s2.replace()总是创建一个new字符串对象,将新的字符串对象分配给s2,然后处置s2曾经指向的字符串对象.所以即使你做了s = s.replace('one', 'one'),你仍然会得到一个新的字符串对象。
      【解决方案10】:

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

      如果你使用两个相同的字符串,与其创建两个字符串对象浪费内存,所有相同内容的interned字符串都指向同一个内存。

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

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

      【讨论】:

        【解决方案11】:

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

        来自语言参考:

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

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

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

        【讨论】:

          【解决方案12】:

          == 运算符测试值等价性。 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
          

          【讨论】:

            【解决方案13】:

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

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

            is 检查地址级别等价性

            == 检查值级别等价

            【讨论】:

              【解决方案14】:

              is 是身份测试,== 是平等测试(请参阅the Python documentation)。

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

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

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

              【讨论】:

                【解决方案15】:

                在解决这个问题时,我们必须明确的基本概念是了解 is== 之间的区别。

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

                所以,我们可以说 is 用于比较内存位置。鉴于,

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

                代码

                如果是字符串字面量(没有分配给变量的字符串),内存地址将与图片中所示的相同。所以,id(a)==id(b)。 剩下的这个是不言自明的。

                【讨论】:

                • 你能把你的代码直接贴在代码标签里吗?
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2015-01-09
                • 2019-12-21
                相关资源
                最近更新 更多