想象一下一种叫做FakeMutablePython的语言,在这种语言中,您可以使用列表赋值等方法来改变字符串(比如mystr[0] = 'a')a = "abc"
它在内存地址0x1中创建一个条目,包含“abc”和指向它的标识符a。
现在,说你愿意。。b = a
这将创建标识符b,并将其指向相同的内存地址0x1
现在,如果字符串是可变的,并且您更改了b:b[0] = 'z'
这会将存储在0x1的字符串的第一个字节更改为z。。由于标识符a指向此处,因此该字符串也将更改,因此。。print a
print b
…将同时输出zbc
这可能会导致一些非常奇怪、出乎意料的行为。字典键就是一个很好的例子:mykey = 'abc'
mydict = {
mykey: 123,
'zbc': 321
}
anotherstring = mykey
anotherstring[0] = 'z'
现在在FakeMutablePython中,事情变得相当奇怪——最初字典中有两个键,“abc”和“zbc”。。然后将“abc”字符串(通过标识符anotherstring)更改为“zbc”,这样dict有两个键,“zbc”和“zbc”。。。
解决这个奇怪问题的一个办法是,每当您将一个字符串赋给一个标识符(或将其用作dict键)时,它会将字符串从0x1复制到0x2。
这就避免了上述问题,但是如果您有一个需要200MB内存的字符串呢?a = "really, really long string [...]"
b = a
突然你的脚本占用了400MB的内存?这不太好。
如果我们把它指向同一个内存地址,直到我们修改它呢?Copy on write。问题是,这可能会很复杂。。
这就是不变性的来源。。而不是要求.replace()方法将字符串从内存复制到新地址,然后修改它并返回。。我们只是使所有字符串都不可变,因此函数必须创建一个新字符串才能返回。这解释了以下代码:a = "abc"
b = a.replace("a", "z")
并证明:>>> a = 'abc'
>>> b = a
>>> id(a) == id(b)
True
>>> b = b.replace("a", "z")
>>> id(a) == id(b)
False
(函数^{}返回对象的内存地址)