2012-10-24 183 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]。在另一篇文章中看到我的解釋。 –