Python: 为什么 ("hello" is "hello") 的结果是 True?

63

为什么在Python中 "hello" is "hello" 的结果会是 True

我在这里读到了以下内容:

如果两个字符串字面量相等,则它们被放置在同一内存位置上。字符串是不可变的实体,不会有任何损害。

那么,每个Python字符串在内存中只有一个地方?听起来很奇怪。这里到底发生了什么?


还可以查看id函数以检查内存位置:print id("hello") - Blixt
bzlm,pyref.infogami.com/intern链接已经失效,但archive.org在这里有一个副本:<br/> http://web.archive.org/web/20090429040354/http://pyref.infogami.com/intern <br/>然而,正如@bobince在下面很好地证明的那样,虽然这通常是正确的,但并不总是正确的。 - Dave Burton
7个回答

95

Python(与Java、C、C++和.NET一样)使用字符串池/内部化。解释器意识到“hello”与“hello”相同,因此它会优化并使用内存中的相同位置。

另一个好处:"hell" + "o"是"hello" ==> True


26
即使C/C ++通常会这样做;在C语言中,“foo” ==“foo”通常是正确的。在C和Python中,这是一个实现细节;我认为Python中没有任何东西需要解释器这样做,并且在C/C ++中,这是一种优化,不是所有编译器都可以实现并且可以被禁用。(相比之下,这个属性在Lua中总是成立;所有字符串都是内部化的。) - Glenn Maynard
2
@Glenn,你说得对,我很高兴有人提到了。当然,没有人应该依赖这是真的。 - Kenan Banks
它是一种解释器或编译器,用于像C/C++这样的语言,其特定工作是通过使编译时确定的字符串相同来进行优化。 - andy
1
在这种特定情况下,对象是相同的,因为同一表达式中的两个文字匹配并导致存储在代码中的单个常量。如果你在交互式shell中分别使用a = 'hell' + 'o!'b = 'hello!',那么a is b将会是false。a = 'hell' + 'o'b = 'hello'确实触发了内部化,所以它们是相等的。但是把这两个例子放到一个函数中,你又会得到相同的对象。有多种重用对象的方法,它们无疑是优化的结果。不要依赖于这些实现细节。 - Martijn Pieters

66

那么每个Python字符串在内存中只有一个位置吗?

不是的,只有解释器决定优化的字符串才会被放在唯一的位置上。这个决策是基于并非语言规范的策略做出的,而且在不同的CPython版本中可能会发生变化。

例如,在我的安装(2.6.2 Linux)中:

>>> 'X'*10 is 'X'*10
True
>>> 'X'*30 is 'X'*30
False

对于整数类型同样适用:

>>> 2**8 is 2**8
True
>>> 2**9 is 2**9
False
所以不要指望一个字符串就是那个字符串:即使只看C的实现,也不安全。

15
因此,在字符串相等比较中,您应始终使用== - SingleNegationElimination
Python解释器会缓存小整数(最多256个)。因此,a = 50; b = 50; a is b 是True,a = 500; b = 500; a is b 是False。 - Darshan Chaudhary
@DarshanChaudhary:后面的表达式实际上是True,因为你把所有赋值都放在了一行上。500是一个字面常量,存储在代码对象中,而ab都被赋予了这个常量...再次强调,这只是一个实现细节,不能保证一定成立。 - Martijn Pieters

13

字面字符串可能会根据它们的哈希值或类似的东西进行分组。两个相同的字面字符串将存储在同一段内存中,并且所有引用都指向那个位置。

 Memory        Code
-------
|          myLine = "hello"
|        /
|hello  <
|        \
|          myLine = "hello"
-------

2
这正是被接受的答案所说的... - Martin
3
正如bobince所指出的那样,这并不总是正确的。 - erickrf

6
< p > is 操作符会在两个参数是同一个对象时返回 true。你的结果是由此产生的,以及引用的那一部分。

对于字符串字面值,它们是被池化(interned)的,这意味着它们会与已知的字符串进行比较。如果已经有一个相同的字符串,则该字面值将采用该值,而不是另一个值。因此,它们变成了同一个对象,表达式为真。


它们“变成了同一个对象”?如果你修改其中一个,另一个不会被修改。 - endolith
3
所讨论的对象是已经存储的字符串,而不是被赋值为该字符串的变量。在Python中,无法修改字符串。 - SingleNegationElimination

2

Python解释器/编译器会解析字符串字面量,即带引号的字符列表。当它这样做时,它可以检测到“我之前看过这个字符串”,并使用上次相同的表示。它能够做到这一点,因为它知道以这种方式定义的字符串是不能被更改的。


1

为什么这很奇怪呢?如果字符串是不可变的,只存储一次就有很多意义。.NET也有相同的行为。


1
字符串的内部化与不变性有何关联?在Python和“.NET”中,许多东西都是不可变的,但没有被内部化。 - bzlm
2
因为如果字符串字面量可以在内存中更改,它就无法共享(或“内部化”)。 - harto
确实,由于该对象是不可变的,因此可以安全地共享对实例的引用。 - Brian Rasmussen

0

我认为如果任何两个变量(不仅仅是字符串)包含相同的值,该值将只存储一次而不是两次,并且两个变量都将指向同一个位置。这可以节省内存。


不是这样的!它只涉及字符串和小整数。例如,当你复制一个列表或字典时,尽管它们具有相同的值(== 等式),但它们不是同一个对象("is" 等式)。这就是为什么你可以更改列表的副本,而原始列表保持不变(反之亦然)。在 O'reilly 的《学习 Python》书的动态类型章节中提供了很好的解释。 - fanny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接