可变对象和不可变对象

3

我正在试图理解可变和不可变对象。我已经阅读了字符串是不可变的,并且对于每个字符串,都会创建一个具有不同对象ID的单独对象。我正在尝试使用下面的简单代码来验证这一点,但是我看到多个不同的字符串具有相同的对象ID。请问有人能够澄清这一点吗?先感谢您。

mystring = ""
mylist = ["This ", "That ", "This ", "That ", "This ", "That ", "This ", "That "]

for item in mylist:
    mystring = mystring + item
    print("mystring: ", mystring, "ID of mystring: ", id(mystring))

这将导致以下输出:
mystring:  This  ID of mystring:  6407264
mystring:  This That  ID of mystring:  42523448
mystring:  This That This  ID of mystring:  42523448
mystring:  This That This That  ID of mystring:  6417200
mystring:  This That This That This  ID of mystring:  42785608
mystring:  This That This That This That  ID of mystring:  42785608
mystring:  This That This That This That This  ID of mystring:  42837536
mystring:  This That This That This That This That  ID of mystring:  42775856

id未被使用时,它们会被回收,因此如果您丢弃旧字符串,则看到相同的id并不奇怪。 - AChampion
@AChampion:除非生命周期真的应该重叠,所以ID重用应该是无效的。这里正在进行一种优化,它并没有完全保留语言对id返回值和字符串不可变性的保证。 - user2357112
@user2357112,生命周期不重叠。 - wim
正是因为这里没有重叠,才使得原地优化成为可能。当名称 mystring 重新绑定的时候,旧的 mystring 就不再有引用了。如果保留对旧对象的另一个引用,就无法看到优化效果了! - wim
1
@wim: 没有优化,新的 mystring 值会在名称绑定操作之前存在,然后名称绑定将结束旧的 mystring 值的生命期。+和=之间将存在生命周期重叠。 - user2357112
显示剩余3条评论
2个回答

3

Python允许为生命周期不重叠的对象重用对象ID,但在应存在生命周期重叠的情况下,您看到了ID重用。具体而言,在执行此语句期间:

mystring = mystring + item

在评估mystring + item和分配给mystring之间,mystring的任何两个连续值都应该有生命周期重叠。您看到了连续的mystring值的ID重用,这不应该发生。
您观察到的效果是由于CPython字节码评估循环中的一种优化引起的,其中形式为以下语句:
string1 = string1 + string2

或者

string1 += string2

如果解释器可以确认string1没有其他引用,则尝试通过在原地突变string1来执行连接操作。 您可以在Python/ceval.c中的unicode_concatenate下看到代码。由于引用计数检查,这种优化大多是不可见的,但对id值的影响是它可见的一种方式。


2

字符串是不可变的。不存在可以改变它们的str方法。

话虽如此,你看到同一id多次出现的原因是因为当一个对象不再使用时,Python会重用其在内存中的位置。而id所做的正是通过返回对象在内存中的位置来提供唯一标识符。

一种说服自己这确实是观察结果的原因的方法是始终确保每个创建的字符串都有一个引用,方法是将它们添加到列表中。

代码

mystring = ""
mylist = ["This ", "That ", "This ", "That ", "This ", "That ", "This ", "That "]

# A list to keep a reference to each string
created_strings = []

for item in mylist:
    mystring = mystring + item

    # Prevent mystring from being garbage collected by adding it to the list
    created_strings.append(mystring)

    print("mystring: ", mystring, "ID of mystring: ", id(mystring))

输出

mystring:  This  ID of mystring:  2522900655888
mystring:  This That  ID of mystring:  2522903930416
mystring:  This That This  ID of mystring:  2522903930544
mystring:  This That This That  ID of mystring:  2522902118880
mystring:  This That This That This  ID of mystring:  2522900546624
mystring:  This That This That This That  ID of mystring:  2522900546864
mystring:  This That This That This That This  ID of mystring:  2522902428376
mystring:  This That This That This That This That  ID of mystring:  2522900907952

注意,现在内存不再被回收,每个对象都有不同的id


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