2009-06-10 35 views
11

我在閱讀How to think like a computer scientist這是「Python編程」的入門文本。應用於列表的乘法運算符(數據結構)

我想澄清應用於列表時乘法運算符(*)的行爲。

考慮函數make_matrix

def make_matrix(rows, columns): 
""" 
    >>> make_matrix(4, 2) 
    [[0, 0], [0, 0], [0, 0], [0, 0]] 
    >>> m = make_matrix(4, 2) 
    >>> m[1][1] = 7 
    >>> m 
    [[0, 0], [0, 7], [0, 0], [0, 0]] 
""" 
return [[0] * columns] * rows 

實際產量

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

make_matrix的正確版本是:

def make_matrix(rows, columns): 
""" 
    >>> make_matrix(3, 5) 
    [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] 
    >>> make_matrix(4, 2) 
    [[0, 0], [0, 0], [0, 0], [0, 0]] 
    >>> m = make_matrix(4, 2) 
    >>> m[1][1] = 7 
    >>> m 
    [[0, 0], [0, 7], [0, 0], [0, 0]] 
""" 
matrix = [] 
for row in range(rows): 
    matrix += [[0] * columns] 
return matrix 

之所以make_matrix的第一個版本出現故障(如在9.8書中解釋)是

...每行是其他行的名稱...

我不知道爲什麼

[[0] * columns] * rows 

導致...每行是其他行的名稱...

但不

[[0] * columns] 

即爲什麼連續的每個[0]不是其他行元素的別名。

回答

18

python中的所有東西都是對象,除非明確要求python永遠不會複製副本。

當你

innerList = [0] * 10 

您創建了10個元素,他們都指的同一int對象0列表。

由於整數對象是不可改變,當你做

innerList[1] = 15 

,以便它指的是另一個整數15你正在改變列表的第二個元素。這總是有效的,因爲int對象不變性。

這就是爲什麼

outerList = innerList * 5 

將創建具有5個元素一個list對象,每一個是如以上剛剛相同innerList的參考。但由於list對象是可變

outerList[2].append('something') 

是一樣的:

innerList.append('something') 

因爲他們是兩個引用到相同list對象。所以這個元素以單個list結束。它看起來是重複的,但事實是隻有一個list對象,並有很多引用它。

通過,如果你做

outerList[1] = outerList[1] + ['something'] 

這裏你創建對比另一個list對象(使用+處理列表是一個明確的副本),並指派一提到它變成outerList第二位置。如果以這種方式「追加」元素(不是真的追加,而是創建另一個列表),innerList將不受影響。

-3

列表不是原語,它們通過引用傳遞。列表的副本是指向列表的指針(用C術語表示)。你對列表所做的任何事情都會發生在列表的所有副本和其內容的副本上,除非你做一個淺拷貝。

[[0] * columns] * rows 

糟糕,我們剛剛製作了一大串指向[0]的指針。改變一個,你改變它們。

整數不是通過引用傳遞的,它們實際上是複製的,因此[0] *內容實際上是製作很多NEW 0並將它們附加到列表中。

+0

aha,難道這不像一個大小爲1的特殊行爲類型。 我聽說「Pythonista」不喜歡特殊情況(正如Python中的Zen所解釋的那樣)...特殊情況下aren沒有足夠的特殊性來打破規則......「)。 – fizzbuzz 2009-06-10 11:23:48

+4

誤導。 python中沒有「原始」這樣的東西。一切都是一個對象,並且始終通過引用傳遞,包括INTEGERS。事實上,變量只是名稱引用。這裏的問題是列表是可變的,而整數不是。 – nosklo 2009-06-10 11:32:03