如何定义一个二维数组?

895

我想像这样定义一个没有初始化长度的二维数组:

Matrix = [][]

但是这样会出现错误:

IndexError: 列表索引超出范围


18
我们不能定义数组或其他东西,但可以像这里的答案所示创建多维序列。请记住,在Python中,变量是无类型的,而值是强类型的。 - SingleNegationElimination
2
我有些困惑,从其他编程语言的角度来看:一个包含1D-Array的1D-Array和一个2D-Array之间是有区别的。据我所知,在Python中没有多维数组(或列表)这种数据类型。这一点需要说明一下... - Dirk Reichel
4
请参考Python3 FAQ中关于“如何创建多维列表”的内容。链接:https://docs.python.org/3/faq/programming.html#faq-multidimensional-list - Kevin W Matthews
31个回答

1236

你试图索引一个未初始化的数组。在添加项之前,你必须先用列表初始化外部列表;Python 将其称为“列表推导”。

# Creates a list containing 5 lists, each of 8 items, all set to 0
w, h = 8, 5
Matrix = [[0 for x in range(w)] for y in range(h)] 

#您现在可以向列表中添加项目:

Matrix[0][0] = 1
Matrix[6][0] = 3 # error! range... 
Matrix[0][6] = 3 # valid

请注意,该矩阵是以“y”地址为主的,换句话说,“y索引”出现在“x索引”之前。

print Matrix[0][0] # prints 1
x, y = 0, 6 
print Matrix[x][y] # prints 3; be careful with indexing! 

虽然你可以将它们命名为你希望的任何名称,但为了避免在索引中出现混淆,我建议这样做。如果你同时使用"x"来表示内部和外部列表,并想要一个非方形矩阵,那么可能会产生一些困惑。


250
[[0 for x in range(cols_count)] for x in range(rows_count)] (这是一段Python代码,翻译成中文后仍为代码) - songhir
3
ademar111190 的编辑有些奇怪。在 Python 3 中没有 xrange,但如果你必须使用 Python 2,则 xrange 是正确的函数,如果你不想不必要地创建对象的话。 - Dave
4
如果你不需要零填充,可以使用range直接创建内部列表:[range(5) for x in range(5)] - alanjds
24
@6packkid中[0]*w部分不错,但[[0]*w]*h]将会产生意外的行为。尝试使用mat = [[0]*3 for i in range(3)]; mat[0][1] = 10; print(mat == [[0,10,0],[0,0,0],[0,0,0]])而非mat = [[0]*3]*3; mat[0][1] = 10; print(mat == [[0,10,0],[0,10,0],[0,10,0]]) - senderle
5
@6packkid 这里使用了相同的数组h次(因此更改一个会影响所有行)。例如,尝试test = [[0]*3]*5; test[1][1]=7; print(test) - Marc Van Daele
显示剩余9条评论

475

如果你真的需要矩阵,最好使用numpy。在numpy中,矩阵运算通常使用具有两个维度的数组类型。创建新数组的方法有很多种;其中最有用的之一是zeros函数,它接受一个形状参数,并返回一个给定形状的数组,其值初始化为零:

>>> import numpy
>>> numpy.zeros((5, 5))
array([[ 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.]])

以下是创建二维数组和矩阵的其他方法(为了简洁起见,已删除输出):
numpy.arange(25).reshape((5, 5))         # create a 1-d range and reshape
numpy.array(range(25)).reshape((5, 5))   # pass a Python range and reshape
numpy.array([5] * 25).reshape((5, 5))    # pass a Python list and reshape
numpy.empty((5, 5))                      # allocate, but don't initialize
numpy.ones((5, 5))                       # initialize with ones

numpy提供了一种matrix类型,但是它已经不再推荐用于任何用途,并且可能在未来从numpy中删除。


97
每当您需要矩阵时,您都需要使用numpy。这个答案应该首先出现。 - Pat B
4
题目使用英文单词"matrix"并不意味着应该使用np.matrix来表示矩阵。在NumPy中,表示矩阵的正确方式是使用"array"。 - user2357112
1
@senderle,您能详细说明使用matrix的原因吗?自从引入@运算符以来,似乎比这篇文章写作时少了一个原因。 - jpp
2
@jpp,正如之前的帖子所说,来自Matlab的人可能会发现它有用。但是,numpy文档现在表明该类可能会被弃用并在将来被移除,因此我已经从答案中删除了它。 - senderle
如果矩阵的所有元素都是相同类型的,那么numpy是最优选择。如果元素不是相同类型(例如int、float、bool、str),则需要使用常规的Python矩阵。 - D.L
显示剩余2条评论

399

以下是一种初始化列表的嵌套列表的简短表示方法:

matrix = [[0]*5 for i in range(5)]

不幸的是,将其缩短为5*[5*[0]]之类的内容并不起作用,因为您最终会得到5个相同列表的副本,因此当您修改其中一个时,它们都会发生改变,例如:

>>> matrix = 5*[5*[0]]
>>> matrix
[[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]]
>>> matrix[4][4] = 2
>>> matrix
[[0, 0, 0, 0, 2], [0, 0, 0, 0, 2], [0, 0, 0, 0, 2], [0, 0, 0, 0, 2], [0, 0, 0, 0, 2]]

11
"shortening" 失败的逻辑是什么?为什么 Python 在这种情况下输出相同列表的副本,而在 [0]*5 的情况下输出不同单元格的数组?为什么 "shortening" 失败? 当使用 my_list = [0] * nmy_list *= 2 等操作时,它们都会创建一个包含相同引用的列表。因此,在更改任何一个元素时,所有引用都会被更改。如果您想修改其中一个元素,而不影响其他元素,则需要使用另一种方法来创建列表。为什么 Python 在这种情况下输出相同列表的副本,而在 [0]*5 的情况下输出不同单元格的数组? 在第一种情况下,使用了“”运算符来创建一个具有相同引用的列表,因此更改列表中一个元素将更改所有元素。而在第二种情况下,使用了“”运算符来创建一个具有不同引用的列表,因此更改列表中一个元素不会影响其他元素。 - mike622867
22
上述评论并不完全准确:[0]*5 仍会创建一个包含 5 个指向同一对象的引用的序列,该对象表示数字 0。但您永远不会注意到这一点,因为 0 是不可变的(可以认为 0 表现得像一个值,或者你可以将其视为原始数据类型,因为它是不可变的,所以您永远不会遇到引用相同对象而不是副本的问题。) - dreua
11
更加符合Python风格的写法:[[0] * 5 for _ in range(5)],使用匿名的循环计数器 _ - Jean-François Fabre
2
抛弃的笔记“不幸的是,将其缩短为类似于5*[5*[0]]这样的形式并不真正起作用”,应该在本页的更高位置,因为对许多人来说并不明显。 - jcansell
2
是的,Python3的文档也提到了这一点。 - Brainor
显示剩余6条评论

143

如果你想要创建一个空矩阵,正确的语法是:

matrix = [[]]

如果你想生成一个大小为5的矩阵并填充为0,

matrix = [[0 for i in xrange(5)] for i in xrange(5)]

2
@KorayTugay 因为矩阵是使用Python列表(行)嵌套在另一个列表(列)中表示的。 - elig
3
使用Python 3时,请使用range函数而不是xrange函数。 - Rakesh Chaudhari
2
我认为 matrix = [[]] 需要使用 .append 来实际创建索引。否则,matrix[0][0] = 1 将无法工作。 - bomben

91

如果你只是想要一个二维容器来保存一些元素,那么你可以方便地使用字典:

Matrix = {}

那么你可以这样做:

Matrix[1,2] = 15
print Matrix[1,2]

这段代码可行是因为1,2是一个元组,你将其用作字典的键进行索引。结果类似于一个简单的稀疏矩阵。

正如osa和Josap Valls所指出的那样,您还可以使用Matrix = collections.defaultdict(lambda:0),以便缺失的元素具有默认值0

Vatsal进一步指出,对于大型矩阵,这种方法可能效率不高,并且应仅在代码的非性能关键部分中使用。


2
然后你也可以执行 import collections; Matrix = collections.defaultdict(float),将未初始化的元素替换为零。 - Sergey Orshanskiy
2
如果将元组(1,2)用作键访问字典,最坏情况下的复杂度不会是O(n)吗?因为内部实际上需要对元组进行hash。而使用二维数组来访问索引[1,2]则可以提供O(1)的时间复杂度。所以对于这种情况,使用字典并不是一个好的选择。 - DoOrDoNot
@Vatsal https://wiki.python.org/moin/TimeComplexity 上说平均情况是O(1),但你关于最坏情况是对的。不过,除非你处理的是大量的项目,否则你不会在意这个差异。事实上,我更担心的是内存而不是访问时间。 - enobayram
此外,我们总是尽量避免使用字典,直到算法的总体复杂度等于或大于O(n^2)。因为'n'次O(n)访问将导致O(n^2)的复杂度。 - DoOrDoNot
1
@enobayram,抱歉我不同意。如果进行“n”次最坏情况O(n)访问,则渐近分析将始终给出O(n ^ 2)。而摊销分析可以给出较小的下限。而且摊销和平均情况之间有很大的区别...请在发表任何假设和模糊评论之前参考相关资料。 - DoOrDoNot
@Vatsal 这不是 O(n),而是 O(k),其中 k 是元组的大小,由于在这种情况下它是固定的,因此相当于 O(1)。 - Sandeep Datta

49

在Python中,您将创建一个列表的列表。您不必事先声明维度,但您可以这样做。例如:

matrix = []
matrix.append([])
matrix.append([])
matrix[0].append(2)
matrix[1].append(3)

现在 matrix[0][0] == 2,而 matrix[1][0] == 3。您还可以使用列表推导式语法。此示例两次使用它来构建“二维列表”:

from itertools import count, takewhile
matrix = [[i for i in takewhile(lambda j: j < (k+1) * 10, count(k*10))] for k in range(10)]

6
在第一个例子中,使用extend可以很方便地对列表进行操作:如果您从 m = [[]] 开始,那么可以通过 m[0].extend([1,2]) 来添加内部列表(扩展一行),通过 m.append([3,4]) 来添加外部列表(追加一行),这些操作将使列表变为 [[1, 2], [3, 4]] - askewchan

33

以下是针对那些具有C、CPP和Java背景的初学者的代码

rows = int(input())
cols = int(input())

matrix = []
for i in range(rows):
  row = []
  for j in range(cols):
    row.append(0)
  matrix.append(row)

print(matrix)

你会问,为什么这段代码这么长,还是用Python写的?

很久以前当我对Python不太熟悉时,我看到了一行代码就可以生成二维数组的答案,于是我告诉自己再也不会在Python中使用二维数组了。(那些单行代码真的很吓人,而且我完全不知道Python在做什么。请注意,我不熟悉这些速记方法。)


25

你应该创建一个列表的列表,最好的方法是使用嵌套推导式:

>>> matrix = [[0 for i in range(5)] for j in range(5)]
>>> pprint.pprint(matrix)
[[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]]

在你的[5][5]示例中,你创建了一个包含整数“5”的列表,并尝试访问它的第5个项目,这自然会引发IndexError,因为没有第5个项目:

>>> l = [5]
>>> l[5]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

实际上,row_index('i') 和 column_index('j') 的序列如下: '>>> matrix = [[0 for column_index in range(5)] for row_index in range(5)]' - Aniruddha Kalburgi

25

被接受的答案不错且正确,但我花了一些时间才明白我也可以使用它来创建一个完全空的数组。

l =  [[] for _ in range(3)]

导致

[[], [], []]

17

这是我通常在Python中创建二维数组的方法。

col = 3
row = 4
array = [[0] * col for _ in range(row)]

与在列表推导式中使用两个for循环相比,我发现这种语法易于记忆。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接