5

关于这个问题:

如何在 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]]

出乎意料的结果是,使用索引 [0-6][x] 访问的任何元素都将指向 [7][x] 中的最后一行。数组在解释器中看起来相同,因此我很困惑。第一种方法有什么问题?

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

4

3 回答 3

7

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

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

于 2013-09-21T13:44:52.823 回答
5

在您的第一个版本中,您正在创建一个包含数字 1 的列表,并将其乘以 8 次创建一个包含 8 个 1 的列表,并使用列表 8 次来创建a.

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

于 2013-09-21T13:42:45.187 回答
0

也许重构第一种方式使它更容易理解:

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
于 2013-09-21T14:05:57.950 回答