Python中的可变和不可变

kbl*_*kbl 5 python mutable immutability

我是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
Run Code Online (Sandbox Code Playgroud)

use*_*ica 3

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

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

这种优化基本上很好,因为引用计数检查确保它的行为基本上就像字符串确实不可变并且创建了一个新字符串一样。然而,在 中x = x + stuff,旧字符串和新字符串的生命周期应该短暂重叠,因为新字符串应该在赋值结束旧字符串的生命周期之前就存在,因此ID 值不可能相等。

id这是优化与没有发生字符串突变时明显不同的少数方式之一。语言开发者似乎已经决定他们对此表示同意。