为什么使用“==”或“is”比较字符串有时会产生不同的结果?
- 2024-11-19 08:38:00
- admin 原创
- 8
问题描述:
两个字符串变量被设置为相同的值。s1 == s2
总是返回True
,但s1 is s2
有时返回False
。
如果我打开 Python 解释器并进行相同的比较is
,它会成功:
>>> s1 = 'text'
>>> s2 = 'text'
>>> s1 is s2
True
这是为什么呢?
解决方案 1:
is
是身份测试,是==
相等性测试。代码中发生的事情将在解释器中模拟如下:
>>> a = 'pub'
>>> b = ''.join(['p', 'u', 'b'])
>>> a == b
True
>>> a is b
False
所以,难怪它们不一样,对吧?
换句话说:a is b
相当于id(a) == id(b)
解决方案 2:
这里的其他答案是正确的:is
用于身份比较,而==
用于相等比较。由于您关心的是相等性(两个字符串应该包含相同的字符),因此在这种情况下,is
运算符完全是错误的,您应该使用==
。
之所以is
能够交互工作,是因为大多数字符串文字默认是驻留的。摘自维基百科:
驻留字符串可加快字符串比较速度,这有时会成为严重依赖带有字符串键的哈希表的应用程序(如编译器和动态编程语言运行时)的性能瓶颈。如果不进行驻留,检查两个不同的字符串是否相等需要检查两个字符串的每个字符。此过程很慢,原因如下:字符串长度本质上为 O(n);它通常需要从内存的多个区域读取,这需要时间;并且读取操作会填满处理器缓存,这意味着可用于其他需求的缓存更少。对于驻留字符串,在原始驻留操作之后,只需进行简单的对象标识测试即可;这通常作为指针相等测试来实现,一般只有一条机器指令,根本没有内存引用。
因此,当您的程序中有两个具有相同值的字符串文字(在程序源代码中逐字输入的单词,用引号括起来)时,Python 编译器将自动驻留这两个字符串,使它们都存储在相同的内存位置。(请注意,这种情况并不总是发生,并且发生这种情况的规则非常复杂,因此请不要在生产代码中依赖这种行为!)
由于在交互式会话中,两个字符串实际上都存储在同一内存位置,因此它们具有相同的标识,因此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 的内置函数,但sys
在 Python 3 中被移至模块。
解决方案 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
他们的年龄相同,但不是同一个人。一个字符串可能等同于另一个字符串,但不是同一个对象。
解决方案 6:
这是一个侧注,但在惯用的 Python 中,你经常会看到类似这样的内容:
if x is None:
# Some clauses
这是安全的,因为保证有一个 Null Object (即 None )的实例。
解决方案 7:
如果您不确定自己在做什么,请使用“==”。如果您对此有更多了解,则可以对已知对象(如“无”)使用“is”。
否则,你最终会想知道为什么事情不起作用以及为什么会发生这种情况:
>>> a = 1
>>> b = 1
>>> b is a
True
>>> a = 6000
>>> b = 6000
>>> b is a
False
我甚至不确定在不同的 Python 版本/实现之间是否可以保证某些事情保持不变。
解决方案 8:
根据我有限的 Python 经验,is
用于比较两个对象以查看它们是否是同一个对象,而不是两个具有相同值的不同对象。 ==
用于确定值是否相同。
这是一个很好的例子:
>>> s1 = u'public'
>>> s2 = 'public'
>>> s1 is s2
False
>>> s1 == s2
True
s1
是Unicode字符串,s2
是普通字符串。它们不是同一类型,但它们是同一值。
解决方案 9:
我认为这与以下事实有关:当“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”,如果我这样做了,它就会使它们成为同一个对象。
解决方案 10:
该==
运算符测试值是否相等。该运算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
参考资料:Allen B. Downey 的Think Python 2e
解决方案 11:
我认为这被称为“interned”字符串。Python 会这样做,Java 也会这样做,在优化模式下编译时 C 和 C++ 也会这样做。
如果使用两个相同的字符串,则所有具有相同内容的实习字符串都指向相同的内存,而不是通过创建两个字符串对象来浪费内存。
这导致 Python 的“is”运算符返回 True,因为两个内容相同的字符串指向同一个字符串对象。Java 和 C 中也会发生这种情况。
不过这只是为了节省内存。您不能依赖它来测试字符串相等性,因为各种解释器、编译器和 JIT 引擎并不总是能做到这一点。
解决方案 12:
实际上,is
运算符检查身份,而 == 运算符检查相等性。
来自语言参考:
类型影响对象行为的几乎所有方面。甚至对象身份的重要性在某种意义上也受到影响:对于不可变类型,计算新值的操作实际上可能返回对具有相同类型和值的任何现有对象的引用,而对于可变对象则不允许这样做。例如,在 a = 1; b = 1 之后,a 和 b 可能会或可能不会引用具有值 1 的同一个对象,具体取决于实现,但在 c = []; d = [] 之后,c 和 d 保证引用两个不同的、唯一的、新创建的空列表。(请注意,c = d = [] 将同一个对象分配给 c 和 d。)
所以从上面的陈述我们可以推断出,字符串是不可变类型的,用“is”检查时可能会失败,用“is”检查时可能会成功。
这同样适用于int
和,tuple
它们也是不可变类型。
解决方案 13:
is
将比较内存位置。它用于对象级别的比较。
==
将比较程序中的变量。它用于在值级别进行检查。
is
检查地址级别等效性
==
检查价值水平等同性
解决方案 14:
is
是身份测试和==
相等性测试(参见Python 文档)。
大多数情况下,如果a is b
,则a == b
。但也有例外,例如:
>>> nan = float('nan')
>>> nan is nan
True
>>> nan == nan
False
因此,您只能is
用于身份测试,而不能用于相等性测试。
解决方案 15:
在解决这个问题时,我们必须明确的基本概念是了解is和==之间的区别。
“is” 将比较内存位置。如果 id(a)==id(b),则 a is b 返回 true,否则返回 false。
因此,我们可以说它用于比较内存位置。然而,
==用于相等性测试,这意味着它只比较结果值。下面显示的代码可以作为上述理论的一个例子。
代码
对于字符串文字(未分配给变量的字符串),内存地址将与图片中显示的相同。因此,id(a)==id(b)。其余部分是不言自明的。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件