2013-09-21 30 views
3

关于这个问题:用两个维阵列工作元(阵列)“乘法”与列表理解初始化

How to initialize a two-dimensional array in Python?

虽然,我发现,以某种方式初始化它会产生意想不到的结果。我想了解在初始化这两种方式的8X8格之间的区别:

>>> a = [[1]*8]*8 
>>> a 
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \ 
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \ 
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \ 
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]] 

>>> A = [[1 for i in range(8)] for j in range(8)] 
>>> A 
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \ 
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \ 
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], \ 
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]] 

意想不到的结果,与的indeces [0-6] [访问的任何元素x]将指向[7] [x]中的最后一行。阵列看起来相同的解释,因此我的困惑。第一种方法有什么问题?

如果相关,这些数组持有对GTK EventBoxes的引用,这些引用表示棋盘的正方形。将初始化方法更改为列表理解方法后,方块会正确响应预期的悬停和单击事件。

+2

我怀疑这可能已经被问及回答。我应该看起来更好...... –

+0

因为'[1]'包含一个不可变的,所以你可以在范围(8)中使用'[[1] * 8]来生成所需的结果。 –

+0

@MartijnPieters但是,假设'a = [1,2]'然后'[a] * 5'与'[a对于_范围(5)] ** **是一样的** ** –

回答

1

在你的第一个版本,你要创建一个包含数字1的列表,并通过乘以8倍创建一个包含8个1S的列表,并使用该名单 8次创造a

因此,当您在第一个版本中更改任何内容时,您会在其他地方看到更改。你的问题是你正在重复使用同一个实例,这在第二个版本中不会发生。

+0

我喜欢这个答案的简洁,因为问题不是很复杂。 –

3

当您使用a = [[1]*8]*8创建二维数组时,那么*运算符会为同一个对象创建8个引用。因此,[1]*8意味着创建一个大小为8的数组,其中全部8个元素是相同的对象(相同的参考)。由于所有元素都是相同的引用,更新参考点将更改数组中每个元素的值的值。

使用列表理解A = [[1 for i in range(8)] for j in range(8)]确保您的二维数组中的每个元素都被唯一引用。这样可以避免您看到所有元素同时更新的错误行为。

+1

'[[1] * 8 for j in range(8)]'也应该可以工作,因为数字是不可变的 – Veedrac

+0

也许列表元素是对正在更新的内部变量的引用。无可否认,我无法给你示例实现细节。但是,实际上我可以说,列表中的所有8个指数都是如何引用同一个对象的。 – bpmason1

0

也许重构的第一种方式使其eeasier明白:

one_item = [1] 
row = one_item*8 
matrix = row*8 

,你可以看到,数组的数组有八个引用来排,这意味着

(a[0] is a[1]) and (a[1] is a[2]) ... 

尝试一下本作例子:

a = [[1]*8]*8 
a[0][0] = 2 

b = [[1 for i in range(8)] for j in range(8)] 
b[0][0] = 3 

print a 
print b 

print (a[0] is a[1]) and (a[1] is a[2]) and (a[2] is a[3]) # true 

print (b[0] is b[1]) and (b[1] is b[2]) and (b[2] is b[3]) # false