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 = [[], [], []]
有人可以用通俗易懂的方式解释修改与覆盖对象引用吗?这是我的意思的一个例子:
通过修改对象引用:
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 = [[], [], []]