需要澄清 Python 中字符串的不变性

Clarification needed regarding immutability of strings in Python

字符串在 Python 中是不可变的,但在下面的示例中,一旦我开始将字母连接到初始字符串,就会生成一个新的 ID,这个 ID 将保持不变,直到我将一个新字符串分配给同名 reversedString。根据我对不可变性的理解,在每个连接中都必须分配新的 id,因为字符串是不可变的,不像列表。请澄清相同。

sample = "hello"
print(id(sample)) # 1635882773744

sample += "A"
print(id(sample)) # 1635885488752

sample += "D2"
print(id(sample)) # 1635885488752

sample += "EWWW"
print(id(sample)) # 1635885488752

sample = "R"
print(id(sample)) # 1635795667504

输出:

1635882773744
1635885488752
1635885488752
1635885488752
1635795667504

在 CPython 中,id 与字符串在内存中的存储位置有关。它不表示任何有关可变性的信息。具体如何使用内存是一个内部实现细节。您当然可以深入研究 CPython 内存管理的内部结构以解开 in-depth,但这并不是很有用。 (Im) 可变性可以简单地证明:

>>> foo = 'bar'
>>> baz = foo
>>> id(foo), id(baz)
(4402654512, 4402654512)
>>> foo += 'quux'
>>> print(foo, baz)
barquux bar
>>> foo[1] = 'o'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

使用 += 改变 foo 不会改变 baz,即使它们曾经指代同一个对象。通过订阅 (foo[1] = ...) 直接更改字符不起作用并引发错误。这证明了字符串不变性。在执行期间如何为此分配内存并不重要。

id() 给出存储变量的地址。当然,每次更改该变量时都会发生变化。 id() 不会与您提到的相同。每次都会像说的那样变化。检查地址并不能说明您正在检查字符串的不变性。

有关字符串的更多说明,请检查以下内容 link: https://www.python.org/dev/peps/pep-3137/

Python 解释器似乎有点聪明。它检测到变量 sample 的旧值正在被丢弃,并且它可以重新使用存储旧值的 space。因此,旧值和新值最终具有相同的 id(),即使它们是不同的对象。

如果您将三个 sample += ... 中的每一个替换为

temp = sample
sample += ...

你会发现你每次都会得到不同的 id()。我们正在阻止 Python 立即回收 space,因为另一个变量持有旧值。

字符串是不可变的。您无需“测试”它。一旦一个字符串明显死了,Python 就可以随心所欲地 re-use 它的 space 。你不能指望 id 在所有对象中都是唯一的,无论是活的还是死的。