早先在刷Leetcode的时候,遇到了一个莫名的bug,为什么二维数组的元素没有按自己的预期改变呢?
当我们这样定义一个二维数组的时候,

n = 3
res = [[0] * n] * n

发生了什么?

[0] * n 创建了一个有n个0的list
[[0] * n] * n 创建了一个包含n个同一个listreference

Explanation

我们利用pythonid方法查看对象在内存中的地址。

1D Array

初始化一个指定大小的list很简单:

>>> zeros = [0] * 3
>>> zeros
[0, 0, 0]
>>> id(zeros)
4602255816
>>> id(zeros[0])
4559308816
>>> id(0)
4559308816

可以看到这里创建一维数组zeros的时候,内部复制了3次0,所以其本身具有一个内存地址,元素的地址则和0的地址相同,和我们预期的一样。

2D Array (Wrong)

利用[[0]*n]*n 创建一个二维数组。

>>> zeros2d = [[0]*3]*3
>>> zeros2d
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> zeros2d[0][1] = 1
 [[0, 1, 0], [0, 1, 0], [0, 1, 0]]

这??? 只改变一个元素的值,为什么变了三个,还是相同位置的?
我们看一下它们的地址。

>>> id(zeros2d)
4602725512
>>> print([id(ele) for ele in zeros2d])
[4600869448, 4600869448, 4600869448]

内部的三个一维数组其实是一个

警告:
这里Python对外部的list用了相同的内部list的引用,即本文开篇提到的。
其实从元素的地址看来创建一维和二维都一样,都是复制内部对象的引用。

2D Array (right)

知道了问题所在,那么正确的使用方式应该有如下形式:

zeros2d = [[0] * 3 for _ in range(3)]

References

Wiki
Quora