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

2024-11-19 08:38:00
admin
原创
8
摘要:问题描述:两个字符串变量被设置为相同的值。s1 == s2总是返回True,但s1 is s2有时返回False。如果我打开 Python 解释器并进行相同的比较is,它会成功:>>> s1 = 'text' >>> s2 = 'text' >>> s1 ...

问题描述:

两个字符串变量被设置为相同的值。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)。其余部分是不言自明的。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用