坑python新建二维列表的一个小坑

Posted 囚生CY

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了坑python新建二维列表的一个小坑相关的知识,希望对你有一定的参考价值。

今天复写PIE架构的代码,写完觉得没什么问题,结果怎么跑也跑不通。本来决定跑通就回寝睡觉,一路弄到十二点多,终于地毯式的debug终于找到了问题所在。反正现在精神状态良好,顺便水一篇博客。

我们常常遇到需要初始化一个空的二维列表来存储矩阵数据,如邻接矩阵等二维结构的数据。

那么一般来说我们常规的操作是这样的:

nrows = 3
ncols = 4

array = []

for i in range(nrows):
    array.append([])
    for i in range(ncols):
        array[-1].append(None)

print(array)

# 输出
'''
[[None, None, None, None], [None, None, None, None], [None, None, None, None]]
'''

为了省事你也可以这么写:

nrows = 3
ncols = 4

array = [[None for i in range(ncols)] for j in range(nrows)]

print(array)

# 输出
'''
[[None, None, None, None], [None, None, None, None], [None, None, None, None]]
'''

可惜我这个人有强迫症,第二种写法还是太长了,超到IDE那条分割线的右边了,而我的强迫症迫使我必须把代码注释整齐地写在紧靠着IDE分割线的右侧,所以我就想肯定能有更“短”的写法。

然后我决定这么写:

nrows = 3
ncols = 4

array = [[None]*ncols]*nrows

print(array)

# 输出
'''
[[None, None, None, None], [None, None, None, None], [None, None, None, None]]
'''

简直完美,我的强迫症得到了极大的满足,我成功把代码注释写在紧靠着IDE分割线的右侧了。

为了防止出错,我还特地验证了一下:

nrows = 3
ncols = 4

array1 = [[None for i in range(ncols)] for j in range(nrows)]
array2 = [[None]*ncols]*nrows

print(array1==array2)


# 输出
'''
True
'''

然后我就发现我怎么也跑不通代码了,花了近一个小时地毯式排查,终于发现了问题:

nrows = 3
ncols = 4

array1 = [[None for i in range(ncols)] for j in range(nrows)]
array2 = [[None]*ncols]*nrows

array1[1][1] = 1
array2[1][1] = 1

print("这是array1:\\n",array1)
print("这是array2:\\n",array2)

# 输出
'''
这是array1:
 [[None, None, None, None], [None, 1, None, None], [None, None, None, None]]
这是array2:
 [[None, 1, None, None], [None, 1, None, None], [None, 1, None, None]]
'''

如果是用两重循环新建的二维列表(array1),我去更新某个位置的元素时,发现更新很正常。但是如果是用两个乘号新建的二位列表(array2),我去更新某个位置的元素时,它会把这一列全部更新一遍。

问题出现了总是很容易能解释清楚,本质上“*ncols”是无伤大雅的,因为只是复制了None。但是“*nrows”复制的是列表,这就有问题了,列表的复制是复制其地址,所以这三行一维列表本质上是指向地址相同的完全相同的三个列表。

所以更新一个元素等价于同时更新三行同一位置的元素了。

我之前还在想能这么简单的写还写两重循环不是太蠢了。果然是我太蠢了。

为了省点事情,这么改写就没有问题了:

nrows = 3
ncols = 4

array1 = [[None for i in range(ncols)] for j in range(nrows)]
array2 = [[None]*ncols for i in range(nrows)]

array1[1][1] = 1
array2[1][1] = 1

print("这是array1:\\n",array1)
print("这是array2:\\n",array2)

# 输出
'''
这是array1:
 [[None, None, None, None], [None, 1, None, None], [None, None, None, None]]
这是array2:
 [[None, None, None, None], [None, 1, None, None], [None, None, None, None]]
'''

总之两个教训

  1. 发现问题时,先反思是不是自己有问题。
  2. 强迫症是真的害人。

分享学习共同进步

 

 

 

以上是关于坑python新建二维列表的一个小坑的主要内容,如果未能解决你的问题,请参考以下文章

乘号在python中的用法,用乘号将元素重复在列表中

python中二维列表(list)的初始化

Python 中关于 round 函数的小坑

Python知识点面试小点列表生成式小坑

Python 中关于 round 函数的小坑

python 操作符** (两个乘号就是乘方)