为什么python字符串和元组是不可变的?

use*_*477 49 python string tuples immutability

我不确定为什么字符串和元组是不可变的; 使它们不可变的优点和缺点是什么?

dbr*_*dbr 75

想象一下名为FakeMutablePython的语言,您可以使用列表赋值等方式更改字符串(例如mystr[0] = 'a')

a = "abc"
Run Code Online (Sandbox Code Playgroud)

这会在内存地址0x1中创建一个条目,其中包含"abc"和a指向它的标识符.

现在,说你做..

b = a
Run Code Online (Sandbox Code Playgroud)

这将创建标识符,b并将其指向相同的内存地址0x1

现在,如果字符串是可变的,那么你改变了b:

b[0] = 'z'
Run Code Online (Sandbox Code Playgroud)

z会将存储在0x1的字符串的第一个字节更改为..由于标识符a指向此处,因此该字符串也会更改,因此..

print a
print b
Run Code Online (Sandbox Code Playgroud)

..两者都会输出 zbc

这可能会导致一些非常奇怪的,意外的行为.字典键就是一个很好的例子:

mykey = 'abc'
mydict = {
    mykey: 123,
    'zbc': 321
}

anotherstring = mykey
anotherstring[0] = 'z'
Run Code Online (Sandbox Code Playgroud)

现在在FakeMutablePython中,事情变得相当奇怪 - 你最初在字典中有两个键,"abc"和"zbc"..然后你将"abc"字符串(通过标识符anotherstring)改为"zbc",所以dict有两个键,"zbc"和"zbc"......

这种奇怪的一种解决方案是,无论何时将字符串分配给标识符(或将其用作dict键),它都会将字符串复制为0x1到0x2.

这可以防止上述情况,但是如果你有一个需要200MB内存的字符串呢?

a = "really, really long string [...]"
b = a
Run Code Online (Sandbox Code Playgroud)

突然你的脚本需要400MB的内存?这不是很好.

如果我们将它指向相同的内存地址,直到我们修改它呢?复制写.问题是,这可能很复杂.

这就是不变性的来源.而不是要求.replace()方法将字符串从内存复制到新地址,然后修改它并返回..我们只是使所有字符串不可变,因此该函数必须创建一个新的字符串返回.这解释了以下代码:

a = "abc"
b = a.replace("a", "z")
Run Code Online (Sandbox Code Playgroud)

并证明:

>>> a = 'abc'
>>> b = a
>>> id(a) == id(b)
True
>>> b = b.replace("a", "z")
>>> id(a) == id(b)
False
Run Code Online (Sandbox Code Playgroud)

(该id()函数返回对象的内存地址)

  • 我听过的最好的解释! (2认同)

Nas*_*sir 33

一个是性能:知道字符串是不可变的,这使得在构建时很容易将其布局 - 固定且不变的存储要求.这也是区分元组和列表的原因之一.这也允许实现安全地重用字符串对象.例如,CPython实现使用预分配的对象用于单字符字符串,并且通常返回不更改内容的字符串操作的原始字符串.

另一个是Python中的字符串被视为数字的"元素".没有多少活动会将值8更改为其他任何内容,而在Python中,任何活动量都不会将字符串"8"更改为其他任何内容.

http://effbot.org/pyfaq/why-are-python-strings-immutable.htm


Mar*_*som 9

使它们不可变的一大优点是它们可以用作字典中的键.我确定如果允许更改密钥,字典使用的内部数据结构会变得非常混乱.

  • Buuuuut你可以通过任何用户创建的对象实例来键入,这显然是可变的.那么"密钥"可能只是内存地址,如果字符串是可变的,你仍然可以通过它们唯一的内存地址来键入. (4认同)