Python中的可变与不可变

5
我刚接触Python,正在尝试理解可变和不可变对象之间的差异。Python中可变类型之一是列表(list)。比如说 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

可能是可变 vs 不可变对象的重复问题。 - Devesh Kumar Singh
1
重复的回答并没有解答关于字符串“id”的问题... - hiro protagonist
@hiroprotagonist 但是接着它会改变并且持续改变。 - DYZ
附注:string是一个糟糕的变量名,因为它与string模块名称冲突。 - jpmc26
这可能也与此相关:https://github.com/satwikkansal/wtfpython#-deep-down-were-all-the-same- - hiro protagonist
显示剩余3条评论
1个回答

3

你不应该连续看到相同的ID,但CPython在使用+进行字符串连接时有一个优化,它并不完全遵守所有规则。

当CPython看到形如x = x + somethingx += something的操作时,如果x引用一个字符串,并且x是该字符串的唯一引用,则CPython将使用realloc来扩展字符串,而不是创建一个新的字符串对象。根据可用内存的细节,realloc可以直接调整分配的内存大小,也可以分配新的内存。如果它调整了分配,那么对象的id仍然保持不变。你可以在Python/ceval.c中的unicode_concatenate中看到实现。

这种优化大部分情况下都是可以的,因为引用计数检查确保其行为基本上与字符串实际上是不可变的并创建了一个新字符串的行为相同。但是,在 "x = x + stuff" 中,旧字符串和新字符串应该有短暂的重叠生命周期,因为新字符串应该在赋值结束旧字符串的生命周期之前就已经存在,所以 ID 值相等应该是不可能的。
"id" 是优化与没有发生字符串突变时可观察到的少数方式之一。语言开发人员似乎已经决定他们对此感到满意。

请提供一个参考,证明该语言保证对象的“生存期”应该持续到分配完成,而不是像在语句级别定义后可以在语句执行期间随时丢弃。 - jpmc26
@jpmc26:Python文档没有定义“lifetime”这个术语,但确实使用了这个术语。虽然在语句级别上没有明智的定义方式,但最接近明智的语句级别定义仍将禁止相等的ID值(因为旧字符串和新字符串在同一语句中都是活动的)。 - user2357112
尽管我希望能够得到该术语的实际定义(特别是针对多线程程序,而不是假定有GIL来使事情线性化),但很难争辩支持任何解释,即认为同时存在的两个对象被视为具有非重叠寿命,并且没有优化,则旧字符串和新字符串肯定会同时存在。在名称(重新)绑定发生之前,必须完全评估赋值的RHS。 - user2357112
如果您担心的是语言是否保证赋值语句的左侧不会被提前丢弃,请参阅赋值语句文档,其中指出名称(重新)绑定仅在新对象可用时发生。字符串连接优化通过预先取消绑定名称来欺骗。 - user2357112

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