【问题标题】:Mutable and Immutable Objects可变和不可变对象
【发布时间】:2018-05-29 04:30:05
【问题描述】:

我正在努力研究可变和不可变对象。我已经读过字符串是不可变的,并且对于每个字符串,都会使用不同的对象 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

【问题讨论】:

  • ids 在不使用时会被回收,因此您看到相同的 id 也就不足为奇了,因为您正在丢弃旧字符串。
  • @AChampion:除了生命周期真的应该重叠,所以ID重用应该是无效的。这里有一个优化,它不能完全保留语言对id 返回值和字符串不变性的保证。
  • @user2357112 生命周期不重叠。
  • @wim:在计算mystring + item 和分配给mystring 之间,连续mystring 值的生命周期应该重叠。生命周期重叠不是可传递的,但这并不重要,因为我们看到 ID 重复用于连续的 mystring 值。如果不是mystring = mystring + item的就地优化,就不会发生这种ID复用。
  • @wim:如果不进行优化,新的mystring值会在名称绑定操作之前出现,然后名称绑定会结束旧的mystring值的生命周期。 += 之间会有一个生命周期重叠。

标签: python string immutability


【解决方案1】:

允许 Python 为具有非重叠生命周期的对象重用对象 ID,但是在应该存在生命周期重叠的情况下,您会看到 ID 重用。具体来说,在这条语句的执行过程中:

mystring = mystring + item

mystring + item 的评估和mystring 的赋值之间,mystring 的任意两个连续值之间应该存在生命周期重叠。您看到 mystring 的连续值重复使用 ID,这是不应该发生的。

您看到的效果是由于 CPython 字节码评估循环中的优化造成的,其中形式的语句

string1 = string1 + string2

string1 += string2

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

【讨论】:

    【解决方案2】:

    字符串不可变的。不存在允许对其进行变异的 str 方法。

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

    说服自己这确实是您观察的原因的一种方法是确保始终引用您创建的每个字符串,方法是将它们添加到list

    代码

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-07
      相关资源
      最近更新 更多