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

问题描述 投票:1062回答:14

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

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

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

我在这里想念什么?

python string comparison identity equality
14个回答
1436
投票

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

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

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

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


13
投票

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

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

这会导致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”检查时,可能会检查成功。

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

==操作员测试值等效。 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

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

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

is检查地址级别是否相等

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

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

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

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

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


8
投票

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


4
投票

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


2
投票

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


533
投票

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

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

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

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

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


101
投票

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

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


56
投票

最后一件事,您可以使用intern函数来确保您引用了相同的字符串:

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

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

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


42
投票

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

说您有一个简单的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

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


36
投票

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

if x is None: 
    # some clauses

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


27
投票

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

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

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

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


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是不同的对象,因为解释器没有使用与我最初没有分配它的对象相同的对象到“一个”,如果我有的话,会使它们成为同一对象。

© www.soinside.com 2019 - 2024. All rights reserved.