2012-10-24 141 views
9

我遇到了一些问题,通过在显示的代码中使用Python中的嵌套列表bleow。嵌套列表索引

基本上,我有一个2D列表包含所有0值,我想更新循环中的列表值。

但是,Python不会产生我想要的结果。有什么我误解range()和Python列表索引?

some_list = 4 * [(4 * [0])] 
for i in range(3): 
    for j in range(3): 
     some_list[i+1][j+1] = 1 
for i in range(4): 
    print(some_list[i]) 

结果我的预期是:

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

但是从Python的实际结果是:

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

这是怎么回事?

+0

这里是一个编程惯用Python指南的链接。其中一些已经过时,但有关变量和名称的部分仍然相关:http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables – pcurry

回答

19

问题是由以下事实蟒蛇造成的选择通过引用来传递列表。

常变量传递“按值”,所以他们独立操作:

>>> a = 1 
>>> b = a 
>>> a = 2 
>>> print b 
1 

但因为列表可能会相当大,而不是转移周围的内存整个列表,Python的选择只使用一个参考( 'C'中的'指针')。如果将一个变量分配给另一个变量,则只分配对其的引用。这意味着,你可以有两个变量指向同一个列表存储:

>>> a = [1] 
>>> b = a 
>>> a[0] = 2 
>>> print b 
[2] 

所以,在你的第一行代码,你有4 * [0]。现在[0]是一个指向内存中值0的指针,当你乘上它时,你会得到四个指向内存中相同位置的指针。但是,当你改变这些值之一,那么Python知道指针需要改变,以指向新的价值:

>>> a = 4 * [0] 
>>> a 
[0, 0, 0, 0] 
>>> [id(v) for v in a] 
[33302480, 33302480, 33302480, 33302480] 
>>> a[0] = 1 
>>> a 
[1, 0, 0, 0] 

问题是当你乘这个名单 - 你的列表指针的四个副本。现在,当你改变的一个值在一个列表中,所有四个一起换:

>>> a[0][0] = 1 
>>> a 
[[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]] 

解决的办法是避免第二乘法。一个循环完成这项工作:

>>> some_list = [(4 * [0]) for _ in range(4)] 
+2

感谢您的深入解释! –

+6

虽然这里的代码解决了OP的问题,并且解释正确地指出这是由'指针'传递的列表,但我仍然觉得仅仅因为前两行就会被迫退缩。 Python中的变量不是'通常按值传递'的。 Python中的所有东西都是通过'指针'传递的,但是int和字符串(例如)是不可变的,所以Python对这些类型的行为可以被认为*有效地等同于为几乎所有真实世界的目的传递值。 –

+0

当然,上述评论中的'几乎'是因为您不会通过将相同的巨大字符串分配给多个变量或将其作为参数传递给函数来显着增加内存使用量 - 如果大量字符串是按价值传递。 –

8

实际上,在列表中的所有对象都是一样的,所以改变一个人改变别人太:

In [151]: some_list = 4 * [(4 * [0])] 

In [152]: [id(x) for x in some_list] 
Out[152]: [148641452, 148641452, 148641452, 148641452] 

In [160]: some_list[0][1]=5 #you think you changed the list at index 0 here 

In [161]: some_list 
Out[161]: [[0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0]] #but all lists are changed 

创建列表是这样的:

In [156]: some_list=[[0]*4 for _ in range(4)] 

In [157]: some_list 
Out[157]: [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] 

In [158]: [id(x) for x in some_list] 
Out[158]: [148255436, 148695180, 148258380, 148255852] 

In [163]: some_list[0][1]=5 

In [164]: some_list 
Out[164]: [[0, 5, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] #works fine in this case 
+0

感谢您!这真的很有帮助! –

+0

@KenMa很高兴帮助。 :) –

+2

你做了一个不必要的扩展[0 for _ in range(4)] - 你可以使用4 * [0]。在另一篇文章中看到我的解释。 –