两个字符串变量被赋予相同的值。s1 == s2
永远返回 True
,但是 s1 is s2
有时会返回 False
。
如果我在Python解释器中执行同样的 is
比较,它会成功:
>>> s1 = 'text'
>>> s2 = 'text'
>>> s1 is s2
True
为什么会这样?
两个字符串变量被赋予相同的值。s1 == s2
永远返回 True
,但是 s1 is s2
有时会返回 False
。
如果我在Python解释器中执行同样的 is
比较,它会成功:
>>> s1 = 'text'
>>> s2 = 'text'
>>> s1 is s2
True
为什么会这样?
is
是用于身份测试的,而 ==
是用于相等性测试的。你代码中发生的情况在解释器中会被模拟成:
>>> a = 'pub'
>>> b = ''.join(['p', 'u', 'b'])
>>> a == b
True
>>> a is b
False
因此,他们不相同也就不足为奇了,对吧?
换句话说:a is b
等价于 id(a) == id(b)
==
和.equals()
之间的区别。最棒的部分是Python中的==
与Java中的==
不相似。 - TylerNone
值,因此它始终具有相同的标识符ID。 - SilentGhostis 用于身份比较,而 ==
用于相等性比较。由于您关心的是相等性(两个字符串应包含相同的字符),因此在这种情况下,is 运算符是错误的,您应该改用 ==
。
is 之所以可以交互使用,是因为(大多数)字符串文字默认情况下会被内部化。来自Wikipedia:
内部化的字符串加速了字符串比较,这在某些应用程序中(例如依赖具有字符串键的哈希表的编译器和动态编程语言运行时)是性能瓶颈。
没有内部化,检查两个不同的字符串是否相等涉及检查两个字符串的每个字符。由于以下几个原因,这很慢:它本质上是O(n)长的字符串;通常需要从多个存储区读取,需要时间;阅读填充处理器高速缓存,这意味着其他需求可用的缓存更少。有了内部化的字符串,最初的内部化操作后一个简单的对象标识测试就足够了。这通常实现为指针等式测试,通常只是一个机器指令,根本没有内存引用。
因此,当您的程序中有两个具有相同值的字符串文字(字面上输入到您的程序源代码中的单词,用引号括起来)时,Python编译器会自动将字符串内部化,使它们都存储在相同的内存位置。 (请注意,这不一定总会发生,并且关于何时发生此操作的规则非常复杂,因此请勿在生产代码中依赖此行为!)
由于在交互式会话中,这两个字符串实际上存储在同一内存位置,它们具有相同的身份(identity),因此is
运算符按预期工作。但是,如果您通过其他方法构造一个字符串(即使该字符串包含完全相同的字符),则该字符串可能相等,但它不是同一字符串--也就是说,它具有不同的身份(identity),因为它存储在内存中的另一个位置。
==
或is
。如果您关心字符串是否相等(即具有相同的内容),则应始终使用==
。如果您关心任何两个Python名称是否引用相同的对象实例,则应使用is
。如果您编写的代码处理许多不同的值而不关心它们的内容,或者如果您知道只有一个某物并且想要忽略其他伪装成该物体的对象,则可能需要is
。如果您不确定,请始终选择==
。 - Daniel Prydenis
关键字用于测试对象的身份(identity),而==
用于值比较。
如果使用 is
,只有当对象是同一个对象时结果才为true。然而,只要对象的值相同,==
的结果就为true。
需要注意的是,您可以使用sys.intern
函数来确保获取到的是同一个字符串的引用:
>>> from sys import intern
>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True
正如之前的回答中指出的那样,你不应该使用is
来判断字符串是否相等。但如果你有某种奇怪的要求需要使用is
,这可能会有所帮助。
请注意,在Python 2中使用的intern
函数曾经是内置函数,但在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
他们同龄,但不是同一实例的人。一个字符串可能等价于另一个字符串,但它们并不是同一个对象。
jack1.age = 99
,那不会改变jack2.age
。这是因为它们是两个不同的实例,所以jack1不等于jack2
。然而,如果它们的名字和年龄相同,它们可以相等jack1 == jack2
。对于字符串来说,情况会更加复杂,因为在Python中字符串是不可变的,并且Python经常重用相同的实例。我喜欢这个解释,因为它使用了简单的情况(普通对象)而不是特殊情况(字符串)。 - Flimm如果您不确定自己在做什么,请使用“==”。
如果您对此有更多了解,可以对已知对象如“None”使用“is”。
否则,您最终会想知道为什么事情不起作用以及为什么会发生这种情况:
>>> a = 1
>>> b = 1
>>> b is a
True
>>> a = 6000
>>> b = 6000
>>> b is a
False
我甚至不确定在不同的Python版本/实现之间是否有一些保证会保持不变。
根据我对Python的有限经验,is
用于比较两个对象是否为同一对象,而不是两个具有相同值的不同对象。 ==
用于确定值是否相同。
这里有一个很好的例子:
>>> s1 = u'public'
>>> s2 = 'public'
>>> s1 is s2
False
>>> s1 == s2
True
s1
是一个 Unicode 字符串,而 s2
是一个普通字符串。它们虽然不是相同类型,但却有相同的值。
<type 'unicode'>
)和一个非Unicode字符串(<type 'str'>
)。这是Python 2特定的行为。在Python 3中,s1
和s2
都是str
类型,并且is
和==
都返回True
。 - 0 _>>> s = 'one'
>>> s2 = 'two'
>>> s is s2
False
>>> s2 = s2.replace('two', 'one')
>>> s2
'one'
>>> s2 is s
False
>>>
在这个例子中,我创建了一个名为s2的字符串对象,并将其赋值为'one',但它不是与s
相同的对象,因为解释器并没有使用与我最初未将其分配为'one'时所用的相同对象,如果我这样做了,它们就是相同的对象。.replace()
作为示例可能并不是最好的,因为它的语义可能会让人感到困惑。无论如何,s2 = s2.replace()
都将始终创建一个新的字符串对象,将新的字符串对象分配给 s2
,然后处理 s2
原来指向的字符串对象。因此,即使你执行 s = s.replace('one', 'one')
,你仍将得到一个新的字符串对象。 - Daniel Pryden==
运算符测试值的等价性。 is
运算符测试对象的身份,Python 测试这两个是否真的是同一个对象(即在内存中存在于同一地址)。
>>> a = 'banana'
>>> b = 'banana'
>>> a is b
True
在这个例子中,Python只创建了一个字符串对象,同时a
和b
都指向它。原因是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
参考资料: Think Python 2e作者: Allen B. Downey
input = raw_input("Decide (y/n): ")
来读取控制台输入时,也会出现这个问题。在这种情况下,“y”作为输入并使用if input == 'y':
会返回“True”,而使用if input is 'y':
会返回False。 - Semjon Mössinger