2011-05-10 114 views
17

比方说,我有以下代码:Python列表混乱

a_list = [[0]*10]*10 

这会产生以下列表:

[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] 

然后我想修改的第一个元素在第一列表:

a_list[0][0] = 23 

我预计只有列表的第一个元素被修改,但实际上每个列表的第一个元素被改变了:

[[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0]] 

我设法找到另一种方式来表示我的数据,以避免这种情况,但为什么会发生这种情况?为什么不只是第一个列表发生了变化?当我做第二个*10时,Python是否真的复制了第一个列表的地址,而不是分配一个新的内存块?

+1

您的怀疑是正确的。 – 2011-05-10 22:47:20

+3

“名单混淆”是“名单理解”中鲜为人知的双胞胎吗? ;) – 2011-05-10 22:56:46

回答

13

您对复制地址的预感是正确的。想想这样:

sub_list = [0] * 10 
a_list = [sub_list] * 10 

此代码实际上等同于您在上面张贴的代码。这意味着,只要您更改a_list的任何元素,您实际上都会更改相同的列表sub_list。你甚至可以通过键入来确认它:

a_list = [[0] * 10] * 10 
for n in a_list: 
    print id(n) 

它会显示每个元素相同。为了解决这个问题,你应该使用:

a_list = [[0] * 10 for _ in range(10)] 

为了创建为a_list每一个元素的新子表。

+0

谢谢。第一个例子帮助我更好地理解这一点。 – yoshi 2011-05-10 22:58:53

3

为什么不只是第一个列表更改?

原因很简单,真的是只有1列表,而不是10 - 就像你已经怀疑:

In [1]: [[0]*10]*10 
Out[1]: 
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] 

In [2]: map(id, _) 
Out[2]: 
[54094624, 
54094624, 
54094624, 
54094624, 
54094624, 
54094624, 
54094624, 
54094624, 
54094624, 
54094624] 

如果要创建10名列表,你可以很容易地通过一个表达式实现这一目标像

[[0]*10 for x in xrange(10)] 
4

列表包含对象的引用。列表上的乘法只是重复引用(对同一对象!)。虽然这对于不可变对象(如整数)来说很好,但您得到的是多个引用的同一列表

使用此模式创建单独的列表[[0]*10 for _ in xrange(10)]

+0

但是为什么这不会发生在第一个列表“[0] * 10”?这也是一个列表乘法,但显然这里不仅仅是对第一个被重复的'0'的引用......? – 2012-12-06 17:33:04

+0

@RolfBartstra,它*与重复的**不可变**整数对象的引用相同。你不能改变整数对象的内容,所以你不能像使用可变列表对象那样引起同样的麻烦。 – 2012-12-06 20:10:52