2011-05-10 120 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