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

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

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

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

我在这里想念什么?

答案

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

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

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

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

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

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

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

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

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

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

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

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

>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True

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

请注意,内部函数已从内置函数移至 Python 3 的sys模块中。

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

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

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

if x is None: 
    # some clauses

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

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

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

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

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

从我与蟒蛇的经验有限, is用来比较两个对象,看看他们,而不是两个不同的对象具有相同的值相同的对象。 ==用于确定值是否相同。

这是一个很好的例子:

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

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

我认为这与以下事实有关:当 “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'。 ,如果有的话,它们会使它们成为同一对象。

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

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

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

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