【问题标题】:Mutable and Immutable in PythonPython中的可变和不可变
【发布时间】:2019-05-09 05:44:25
【问题描述】:

我是 Python 新手,并试图了解可变对象和不可变对象之间的区别。 Python 中的可变类型之一是列表。假设 L = [1,2,3],那么 L 有一个指向对象 [1,2,3] 的 id。如果 [1,2,3] 的内容被修改,那么 L 仍然保留相同的 id。换句话说,即使对象的大小和内容已经改变,L 仍然与同一个对象相关联。

对于不可变对象,我的理解是不允许修改对象。因此,如果一个变量被重新分配了一个新值,那么该变量将绑定到一个具有不同 id 的新对象。我希望字符串以类似的方式表现。然而我试图修改一个字符串,但字符串 id 没有改变。

string = "blue"
for i in range(10):
    string = string + str(i)
    print("string id after {}th iteration: {}".format(i,id(string)))


string id after 0th iteration: 46958272
string id after 1th iteration: 46958272
string id after 2th iteration: 46958272
string id after 3th iteration: 47077400
string id after 4th iteration: 47077400
string id after 5th iteration: 47077400
string id after 6th iteration: 47077400
string id after 7th iteration: 47077400
string id after 8th iteration: 47077400
string id after 9th iteration: 47077400

【问题讨论】:

  • 副本并没有回答关于字符串的ids 的问题...
  • 如果以空字符串开头; id 保持不变,直到第 6 次迭代...
  • @hiroprotagonist 但随后它会发生变化并不断变化。
  • 旁白:string 是一个糟糕的变量名称,因为它与 string 模块名称冲突。

标签: python immutability mutable


【解决方案1】:

您确实不应该连续两次看到相同的 ID,但是 CPython 对 + 的字符串连接进行了优化,它并没有完全遵守它应该遵守的所有规则。

当 CPython 看到 x = x + somethingx += something 形式的操作时,如果 x 引用一个字符串并且 x 持有 only 对该字符串的引用,那么 CPython 将会增长realloc 的字符串,而不是创建一个新的字符串对象。根据可用内存的详细信息,realloc 可能会调整已分配内存的大小,也可能分配新内存。如果它调整分配的大小,对象的id 保持不变。您可以在Python/ceval.c 中看到unicode_concatenate 中的实现。

这种优化mostly 很好,因为 refcount 检查确保它的行为 mostly 就好像字符串真的不可变并且创建了一个新字符串一样。然而,在x = x + stuff 中,旧字符串和新字符串应该有短暂重叠的生命周期,因为新字符串应该在赋值结束旧字符串的生命周期之前就已经存在,所以它应该 ID 值不可能相等。

id 是优化与未发生字符串突变时明显不同的少数几种方式之一。语言开发人员似乎已经决定他们可以接受。

【讨论】:

  • 请提供一个参考,该语言保证对象的“生命周期”应该持续到分配完成,而不是其他东西,例如在语句级别定义以便它可以被丢弃声明期间的任何时间。
  • @jpmc26:Python 文档没有定义术语“生命周期”,尽管它确实使用了该术语。但是,没有明智的方法来定义它的语句级别,并且最接近合理的语句级别定义的方法仍然会禁止相同的 ID 值(因为旧字符串和新字符串在同一语句中仍然有效)。
  • 虽然我想要一个术语的实际定义(特别是对于多线程程序,没有假设 GIL 来线性化事物),但很难为两个对象同时存在的任何解释争论time 被认为具有不重叠的生命周期,如果没有优化,新旧字符串肯定会同时存在。必须在名称(重新)绑定发生之前对分配的 RHS 进行全面评估。
  • 如果您担心语言是否保证分配的 LHS 不会被提前丢弃,请参阅 assignment statement documentation,它说名称(重新)绑定仅在新对象可用时发生。字符串连接优化通过预先取消绑定名称来作弊。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多