Python|正确使用二维数组/列表
Python提供了许多创建二维列表/数组的方法。然而,人们必须知道这些方式之间的区别,因为它们会在代码中产生难以追踪的复杂性。让我们从创建以 0 初始化的大小为 N 的一维数组的常用方法开始。
方法 1a
Python3
# First method to create a 1 D array
N = 5
arr = [0]*N
print(arr)
Python3
# Second method to create a 1 D array
N = 5
arr = [0 for i in range(N)]
print(arr)
Python3
# Using above first method to create a
# 2D array
rows, cols = (5, 5)
arr = [[0]*cols]*rows
print(arr)
Python3
# Using above second method to create a
# 2D array
rows, cols = (5, 5)
arr = [[0 for i in range(cols)] for j in range(rows)]
print(arr)
Python3
# Using above second method to create a
# 2D array
rows, cols = (5, 5)
arr=[]
for i in range(rows):
col = []
for j in range(cols):
col.append(0)
arr.append(col)
print(arr)
Python3
# Python 3 program to demonstrate working
# of method 1 and method 2.
rows, cols = (5, 5)
# method 2a
arr = [[0]*cols]*rows
# lets change the first element of the
# first row to 1 and print the array
arr[0][0] = 1
for row in arr:
print(row)
# outputs the following
#[1, 0, 0, 0, 0]
#[1, 0, 0, 0, 0]
#[1, 0, 0, 0, 0]
#[1, 0, 0, 0, 0]
#[1, 0, 0, 0, 0]
# method 2b
arr = [[0 for i in range(cols)] for j in range(rows)]
# again in this new array lets change
# the first element of the first row
# to 1 and print the array
arr[0][0] = 1
for row in arr:
print(row)
# outputs the following as expected
#[1, 0, 0, 0, 0]
#[0, 0, 0, 0, 0]
#[0, 0, 0, 0, 0]
#[0, 0, 0, 0, 0]
#[0, 0, 0, 0, 0]
Python3
rows, cols = (5, 5)
arr = [[0 for i in range(cols)] for j in range(rows)]
Python3
rows, cols = (5, 5)
# method 2b
arr = [[0 for i in range(cols)] for j in range(rows)]
# check if arr[0] and arr[1] refer to
# the same object
print(arr[0] is arr[1]) # prints False
# method 2a
arr = [[0]*cols]*rows
# check if arr[0] and arr[1] refer to
# the same object
# prints True because there is only one
# list object being created.
print(arr[0] is arr[1])
[0, 0, 0, 0, 0]
方法 1b
Python3
# Second method to create a 1 D array
N = 5
arr = [0 for i in range(N)]
print(arr)
[0, 0, 0, 0, 0]
扩展上述内容,我们可以通过以下方式定义二维数组。
方法 2a
Python3
# Using above first method to create a
# 2D array
rows, cols = (5, 5)
arr = [[0]*cols]*rows
print(arr)
[[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]]
方法 2b
Python3
# Using above second method to create a
# 2D array
rows, cols = (5, 5)
arr = [[0 for i in range(cols)] for j in range(rows)]
print(arr)
[[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]]
方法 2c
Python3
# Using above second method to create a
# 2D array
rows, cols = (5, 5)
arr=[]
for i in range(rows):
col = []
for j in range(cols):
col.append(0)
arr.append(col)
print(arr)
[[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]]
到目前为止,这两种方式都给出了看似相同的输出。让我们更改方法 2a 和方法 2b 的数组中的一个元素。
Python3
# Python 3 program to demonstrate working
# of method 1 and method 2.
rows, cols = (5, 5)
# method 2a
arr = [[0]*cols]*rows
# lets change the first element of the
# first row to 1 and print the array
arr[0][0] = 1
for row in arr:
print(row)
# outputs the following
#[1, 0, 0, 0, 0]
#[1, 0, 0, 0, 0]
#[1, 0, 0, 0, 0]
#[1, 0, 0, 0, 0]
#[1, 0, 0, 0, 0]
# method 2b
arr = [[0 for i in range(cols)] for j in range(rows)]
# again in this new array lets change
# the first element of the first row
# to 1 and print the array
arr[0][0] = 1
for row in arr:
print(row)
# outputs the following as expected
#[1, 0, 0, 0, 0]
#[0, 0, 0, 0, 0]
#[0, 0, 0, 0, 0]
#[0, 0, 0, 0, 0]
#[0, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
我们希望只有第一行的第一个元素更改为 1,但在方法 2a 中每行的第一个元素都更改为 1。这种特殊的功能是因为Python使用我们将尝试理解的浅表。
在方法 1a 中, Python不会创建 5 个整数对象,而是仅创建一个整数对象,并且数组 arr 的所有索引都指向同一个 int 对象,如图所示。
如果我们将第 0 个索引分配给另一个整数,比如 1,则创建一个值为 1 的新整数对象,然后第 0 个索引现在指向这个新的 int 对象,如下所示
类似地,当我们创建一个二维数组为“arr = [[0]*cols]*rows”时,我们本质上是对上述类比的扩展。
1. 只创建一个整数对象。
2. 创建一个 1d 列表,它的所有索引都指向点 1 中的同一个 int 对象。
3. 现在,arr[0]、arr[1]、arr[2] ...。 arr[n-1] 都指向上面第 2 点中的同一个列表对象。
上面的设置可以在下图中可视化。
现在让我们将“arr”第一行中的第一个元素更改为
arr[0][0] = 1
=> arr[0] 指向我们上面创建的单个列表对象。(记住 arr[1], arr[2] …arr[n-1] 也都指向同一个列表对象)
=> arr[0][0] 的赋值将创建一个值为 1 的新 int 对象,并且 arr[0][0] 现在将指向这个新的 int 对象。(arr[1][0] 也是如此, arr[2][0] …arr[n-1][0])
这可以在下图中清楚地看到。
因此,当像这样创建 2d 数组时,更改某一行的值将影响所有行,因为数组的所有行都引用了一个整数对象和一个列表对象。
正如您所料,追踪由这种浅表使用引起的错误是很困难的。因此,声明二维数组的更好方法是
Python3
rows, cols = (5, 5)
arr = [[0 for i in range(cols)] for j in range(rows)]
与方法 2a 不同,此方法创建 5 个单独的列表对象。检查这一点的一种方法是使用“is”运算符来检查两个操作数是否引用同一个对象。
Python3
rows, cols = (5, 5)
# method 2b
arr = [[0 for i in range(cols)] for j in range(rows)]
# check if arr[0] and arr[1] refer to
# the same object
print(arr[0] is arr[1]) # prints False
# method 2a
arr = [[0]*cols]*rows
# check if arr[0] and arr[1] refer to
# the same object
# prints True because there is only one
# list object being created.
print(arr[0] is arr[1])
False
True