想象一下一种名为FakeMutablePython的语言,您可以在其中使用列表分配等更改字符串(例如id())
a = "abc"
这会在内存地址0x1的内存中创建一个包含“ abc”的条目,并指向该标识符的标识符id()。
现在,说你做..
b = a
这将创建标识符id(),并将其指向相同的内存地址0x1
现在,如果字符串是可变的,则您更改id():
b[0] = 'z'
这会将存储在0x1的字符串的第一个字节更改为id()。由于标识符a指向此处,因此该字符串也将更改。
print a
print b
..将同时输出id()
这可能会导致某些真正奇怪的意外行为。 字典键将是一个很好的例子:
mykey = 'abc'
mydict = {
mykey: 123,
'zbc': 321
}
anotherstring = mykey
anotherstring[0] = 'z'
现在在FakeMutablePython中,事情变得很奇怪-字典中最初有两个键“ abc”和“ zbc”。然后,将“ abc”字符串(通过标识符id())更改为“ zbc”,因此字典具有 两个键,“ zbc”和“ zbc” ...
解决此怪异问题的一种方法是,每当您为标识符分配字符串(或将其用作dict键)时,它都会将字符串从0x1复制到0x2。
这样可以防止上述情况,但是如果您的字符串需要200MB的内存怎么办?
a = "really, really long string [...]"
b = a
您的脚本突然占用了400MB的内存? 这不是很好。
如果在修改它之前将其指向相同的内存地址该怎么办? 写时复制。 问题是,这样做可能非常复杂。
这就是不可变性的原因。而不是要求id()方法将内存中的字符串复制到新地址,然后对其进行修改并返回。 。 这说明了以下代码:
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
(id()函数返回对象的内存地址)