Python - 修改与覆盖对象引用

Python - modify vs. overwrite an object reference

有人可以用通俗易懂的方式解释修改与覆盖对象引用吗?这是我的意思的一个例子:

通过修改对象引用:

nested_list = [[]]*3
nested

result:
[[], [], []]
# now let me **modify** the object reference
nested[1].append('zzz')

result:
[['zzz'], ['zzz'], ['zzz']]

通过覆盖对象引用:

nested_list = [[]]*3
nested

result:
[[], [], []]
# now let me **modify** the object reference
nested[1] = ['zzz']

result:
[[], ['zzz'], []]

这是否意味着在使用 "append" 时我们只是在使用赋值时修改对象引用,即

nested[1] = ['zzz']

我们正在覆盖该值并将 nested[1] 分配给新的对象引用?是"append"方法和赋值的根本区别造成的吗?如果有,有什么区别?

让我们将名称 x 分配给一个空列表,以便更容易推理代码。

在你的第一个例子中

>>> x = []
>>> nested = [x]*3
>>> nested
[[], [], []]

您正在创建一个列表 nested,其中包含对 x 的三个引用。证明如下:

>>> all(e is x for e in nested)
True

我们只创建了一个空列表 x,这就是为什么

nested[0].append('zzz')
nested[1].append('zzz')
nested[2].append('zzz')

x.append('zzz')

都是等价的并且将 追加到内存中的同一个列表 :

>>> nested[0].append('zzz')
>>> nested
[['zzz'], ['zzz'], ['zzz']]
>>> nested[1].append('zzz')
>>> nested
[['zzz', 'zzz'], ['zzz', 'zzz'], ['zzz', 'zzz']]
>>> nested[2].append('zzz')
>>> nested
[['zzz', 'zzz', 'zzz'], ['zzz', 'zzz', 'zzz'], ['zzz', 'zzz', 'zzz']]
>>> x.append('zzz')
>>> nested
[['zzz', 'zzz', 'zzz', 'zzz'], ['zzz', 'zzz', 'zzz', 'zzz'], ['zzz', 'zzz', 'zzz', 'zzz']]

第二个例子很简单。您创建了一个列表 nested,它最初包含对同一个空列表的三个引用。

然后你覆盖nested的第二个元素(即nested[1])通过发出

指的是什么
>>> x = []
>>> nested = [x]*3
>>> nested[1] = ['zzz']
>>> nested
[[], ['zzz'], []]

nested的第二个元素是一个新列表,与nested的第一个和第三个元素无关。

>>> nested[0] is nested[1]
False
>>> nested[2] is nested[1]
False
>>> nested[0] is nested[2]
True

由于您没有修改 nested[0]nested[2] 引用,它们仍然持有相同的空列表(在我们的示例中也使用名称 x)。

>>> x.append('x')
>>> nested
[['x'], ['zzz'], ['x']]

正如我在评论中所写,list 上的 * 运算符只是复制列表中的引用:

nested_list = [[]] * 3

nested_list 中的所有三个元素都引用同一个列表。如果您考虑上面的表达式真正表达的是什么,这是有道理的。评估确实按以下顺序发生:

nested_list = [[]]  # first create a list with an empty list.
nested_list = nested_list * 3  # duplicate the references to that empty list

到你问题的第二部分。如果用新列表替换第二个元素:

nested_list[1] = ['zzz']

第一个和第三个元素引用同一个空列表,但刚刚分配给 (['zzz']) 的是一个新列表(有一个元素,'zzz'。)

例如如果您执行以下操作,您将看到第一个和第三个仍然引用同一个列表:

nested_list[0].append('a')
print(nested_list)
# [['a'], ['zzz'], ['a']]

解决方案

要创建三个不同的空列表,这可能是您想要的,您通常会做类似的事情(以下几行是等效的):

nested_lists = [[] for _ in range(3)]
nested_lists = [[], [], []]