浅拷贝:为什么列表会改变而字符串不会?

4

我知道当你对一个字典进行浅拷贝时,实际上是复制了引用。所以如果我这样做:

x={'key':['a','b','c']}
y=x.copy()

所以列表['a','b','c']的引用被复制到y中。每当我更改列表(例如x ['key'] .remove('a')),字典x和y都将更改。这部分我理解。但是当我考虑以下情况时:

x={'user':'admin','key':['a','b','c']}
y=x.copy()

当我执行 y['user']='guest' 时,x['user'] 不会改变,但是列表仍然共享同一引用。 所以我的问题是:字符串与列表有何不同?这背后的机制是什么?
2个回答

14
你要做两件不同的事情。当你执行
x['key'].remove('a')

当你改变x['key']指向的对象时,如果另一个变量也指向同一个对象,那么从那个角度来看,你也会看到这个变化:

Pythontutor可视化 Pythontutor可视化2

然而,在第二种情况下,情况就不同了:

PT可视化3

如果你执行以下操作:

y['user']='guest'

你将重新绑定y ['user'] 到一个新的对象。当然,这不会影响x ['user'] 或其引用的对象。

顺便说一下,这与可变和不可变对象无关。如果你做了

x['key'] = [1,2,3]

同样,您也不会更改 y['key']:

PT vis 4

您可以在PythonTutor.com上互动查看其过程。


如果想要更深入地了解这个问题,可以阅读一些好的补充材料,比如《关于Python名称和值的事实与神话》(http://nedbatchelder.com/text/names.html)。 - Steven Rumbalski
如果我把 y['user'] 视为一个指针,它包含了地址,比如说0x0001,指向字符串'admin'的地址。但当我执行 y['user']='guest' 时,Python会为字符串'guest'分配一个新的“内存块”Ox0002,并将y['user']中的地址更新为新地址0x0002吗? - jujae
@jujae:是的,大概就是这样发生的。 - Tim Pietzcker

5

它们的区别在于,在一种情况下,你正在为字典键分配一个新值,而在另一种情况下,你正在修改现有的值。注意你两段代码中的差异:

x['key'].remove('a')

这里没有等号=。你没有在字典中分配任何东西。实际上,字典几乎不知道正在发生什么。你只是从中提取并操作字典中的对象。

y['user'] = 'guest'

在这里,你实际上是在为字典键分配一个新值。
在第二种情况下,你不能执行类似于“remove”的操作,因为字符串是不可变的。然而,区别并不是“因为字符串是不可变的”。区别在于你正在 改变 列表而不是字符串。你可以通过以下方式在第一种情况下获得第二个示例的行为:
x['key'] = ['new', 'list']

这将为 x 中的键分配一个新值,而不影响 y


非常感谢,你的回答和Tim Pietzcker的回答都让我明白了。 - jujae

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