Python中的不可变性

9

我试图理解Python中的不可变性是如何工作的。由于字符串在Python中是不可变的,我期望每次执行字符串操作时,id都会发生变化,但实际上并没有像预期的那样工作。

例如:对t进行的最后一个操作不会改变它的id。任何想法为什么?

操作的屏幕截图


1
我真的很讨厌这种优化方式违反了Python语言的语义。根据语言语义,+=操作符之前和之后的t变量的值应该是不同的对象,它们的生命周期(几乎)没有重叠,因此它们不应该共享一个id - user2357112
2个回答

5

我有一排苹果存储在不同的单元格中 [内存包含变量(我不会进入位级别)],其中一些为空 [单元中包含垃圾/空值]

我拿出一个。它在第3个单元格中 [逻辑地址=3]

我把它涂成蓝色(使用未来技术克隆它以演示不可变性)[对它进行了操作,对整数也可以进行相同的操作]

我看了看要放在哪里,虽然第4个单元格是空的,但第3个单元格也是(因为“原始”苹果不在这里了)!所以我把它放回第3个单元格中 [尽管我们得到了一个“新”的苹果,但它具有相同的地址]


对于你的t(注意id是CPython中变量的内存地址),情况也是如此,但由于我们在这里谈论“一串苹果”(字符串由字符序列组成),我们必须考虑继续序列的空间量,因此如果我的内存看起来像(_代表任意垃圾数据,'^'代表空格)

H e l l o _ _ _ _ _ B O O M
^ string pointer points here

如果我想把字符串改为"Hello you",可以考虑使用空格:

H e l l o ^ y o u _ B O O M
^ string pointer points here

但是,如果我想将字符串更改为"Hello world!",我需要在其他地方寻找"Hello world!"长度的自由空间(我们可能在"BOOM"之后有它,在垃圾回收环境中很可能会这样,看看您的ID如何不同):

H e l l o ^ y o u _ B O O M _ H e l l o ^ w o r l d ! _ G A R B A G E
                              ^ string pointer points here

@B.M. 那里的问题在于与实习类似。我写的基本上就像那边Martijn的评论 - “Python可以自由地重用内存槽。” - Uriel
@B.M. 我不理解你所说的“它不是那样工作的”是什么意思。这个回答所描述的是Python正在为一个新字符串重复使用一个内存地址,因为之前的字符串已经被垃圾回收了。我看了一下链接,但我看不出它如何提供问题描述中现象的另一种解释。 - Rörd
抱歉,我的表达太直接了。我发一些东西来解释我理解的内容。在这个例子中,我认为蓝色的苹果并没有放在第三个单元格里,你只是让绿色的苹果留在那里,在第三个单元格的末尾添加了蓝色的颜料。 - B. M.
1
这是一个糟糕的解释。很难解释行、苹果或单元格应该是什么,它也没有解释为什么我们能“涂掉”不可变的苹果或者为什么第三个单元格是空闲的。 - user2357112
@user2357112 我已将隐喻添加为蓝色注释。虽然我相信这已经非常清楚了。 - Uriel

4

只要原始对象不存在,对象的ID可以被重复使用。这不仅适用于字符串,而是适用于所有Python类型。最简单的例子是,您可以检查一个简单object的ID:

>>> print id(object())
140437485756544
>>> print id(object())
140437485756544

然而,如果我们保留先前对象的引用,该ID将不会被重新使用:
>>> a = object()
>>> id(a)
140437485756544
>>> b = object()
>>> id(b)
140437485756560

您可以通过将中间结果(t 中的值)添加到列表中,来在测试中复制相同的行为。


为什么在将“Test”附加到t的前两个测试用例中,id会发生变化? 这是“随机”的还是Python在这种情况下分配字符串内存的系统方式? - Preston Martin
1
@PrestonM,ID不能保证被重复使用 - 可能有其他东西占用了该内存位置(这是CPython实现id的方式)。保证的是活动对象的ID不会改变,并且没有两个不同的(并且活动的)对象将共享相同的ID。 - user4815162342

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