为什么Python中的'for'循环会改变未被引用的列表?

3
我正在处理一个py脚本,它从csv文件中读取行,对其进行操作并输出。目前我已经实现了将csv转换为列表的功能。
但是我遇到的问题是当我迭代临时列表时,for循环会改变所有的临时列表,而不仅仅是我想要的那一个。以下是我想表达的简单例子。
>>> l = [['hi', 'ho'],['no', 'go']]
>>> t = []
>>> y = []
>>> 
>>> for row in l:
...     row[0] = '123'
...     y.append(row)
...     t.append(row)
... 
>>> y
[['123', 'ho'], ['123', 'go']]
>>> t
[['123', 'ho'], ['123', 'go']]

所以以上内容应该很简单易懂(希望如此)。 (假设我想做除了仅复制列表l之外的其他事情。只是想保持简单)。

但现在这里有一部分我不明白。

>>> z = []
>>> for row in y:
...     row[0] = 'xxxx'
...     z.append(row)
... 
>>> z
[['xxxx', 'ho'], ['xxxx', 'go']]
>>> t
[['xxxx', 'ho'], ['xxxx', 'go']]
>>> y
[['xxxx', 'ho'], ['xxxx', 'go']]

当我想要修改子列表中的第一个部分,并将其保存到一个新的列表 'z' 中时,它会同时修改列表 t!
那么这是怎么回事呢?z、y和t指向相同的内存位置吗?
另外,在这里发生了什么?
>>> for rowx in y:
...     rowx[0] = 'x55x'
...     z.append(rowx)
... 
>>> z
[['xxxx', 'ho'], ['x55x', 'go'], ['x55x', 'go'], ['x55x', 'go']]
>>> t
[['xxxx', 'ho'], ['x55x', 'go']]
>>> y
[['xxxx', 'ho'], ['x55x', 'go']]

类似于上面的问题,为什么y和t会被改变?谢谢!
3个回答

7

Python中只有引用,没有其他。 row 是指向列表 ly 中实际元素的引用。改变 row 会改变该元素,将其添加到另一个对象中会添加原始元素。


4
使用 row[:] 来复制 row - Katriel
@katrielalex:我能给你点赞100次吗?我也遇到了OP的问题,无论如何都想不出如何复制列表而不是复制引用。所有的列表切片都像这样返回一个副本而不是引用吗? - DGH
1
@DGH:对于list,是的。不过只是浅拷贝。 - Ignacio Vazquez-Abrams
谢谢Ignacio的解释。现在我可以在脑海中理解这些列表引用是如何工作的了。还要感谢katrielalex提供的解决方案! - RaytheonLiszt

4
你的三个列表是不同的,但它们之间只有两个元素是共享的:
>>> y[0] is t[0] is z[0]
True
>>> y[1] is t[1] is z[1]
True

如果is运算符告诉你引用指向同一对象,那么对该对象的更改将无论使用哪个引用都会显示出来。
为避免这种情况,如果您想要元素的副本,请使用copy模块。
>>> import copy
>>> a = copy.deepcopy(y)
>>> a
[['xxxx', 'ho'], ['xxxx', 'go']]
>>> a[0] is y[0]
False
>>> a[0][0] = 'copy!'
>>> y
[['xxxx', 'ho'], ['xxxx', 'go']]

对于列表的浅复制,可以使用 y[:] - SingleNegationElimination
谢谢jleedev!虽然[:]回答并解决了我的问题,但你的copy.deepcopy解决方案也是我在脚本中必须执行的操作,因为浅拷贝不够用(显然)。你解决了我的问题! :) - RaytheonLiszt
@雷神公司 [:] 切片、简单的 for 循环和浅拷贝具有完全相同的效果。 - Josh Lee
我也这么认为,但不知道为什么当我在脚本中实现它时,它仍然无法工作。直到我尝试了你的复制解决方案之后,脚本才能正常工作。肯定是我自己的错误,可能由于csv文件或创建脚本时的匆忙所导致的。 - RaytheonLiszt

0

“z、y和t指向同一内存位置吗?”

不是的,但是z[0]、y[0]和t[0]是(虽然不要称其为内存位置,这不是C语言)。您正在将相同的列表['hi','ho']附加到z、y和t上。所以它是同一个列表。如果您不希望它是同一个列表,则必须先复制。


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