背景:
笔者最近在做一个数据统计程序时,需要用到二维数组。为了一行搞定生成同值二维数组,结果遇到了一些小bug,实验了下,终于找到了问题并修复。
前言:
如何简单快速生成 连续10个k的字符串,很简单
'k'*10
#kkkkkkkkkk
那如何快速生成 连续十个True的数组(列表)呢?别再for .... xxx.append()了,不美观也多了好几行代码。一行搞定即可。
[True]*10
#[True, True, True, True, True, True, True, True, True, True]
那如果生成 2x3的全是0的二维数组呢?
按照上面的套路还不简单?也是一行搞定。(笔者也是这么想的,结果bug了。嘛,先看下结果吧,确实是想要的)手到擒来嘛。
[[0]*3]*2
#[[0, 0, 0], [0, 0, 0]]
其实这样有bug!其实这样有bug!其实这样有bug!
案例:
用新旧两种方法生成全是0的2x3数组,修改坐标(1,1)的值为9.
l1 = [[0]*3]*2
l2 = [
[0,0,0],
[0,0,0],
]
print('l1原',l1)
print('l2原',l2)
l1[1][1] = 9
l2[1][1] = 9
print('l1改',l1)
print('l2改',l2)
#结果:
#l1原 [[0, 0, 0], [0, 0, 0]]
#l2原 [[0, 0, 0], [0, 0, 0]]
#l1改 [[0, 9, 0], [0, 9, 0]]
#l2改 [[0, 0, 0], [0, 9, 0]]
土方法正常的改值了。我们帅气的方法(伪)出现bug了。你会发现所有第二列的数值都改成了9。
为什么呢?
这是因为python的机制问题。
在python里,int float str都是深拷贝的,即'x'*n,'x'的值会被复制成n份;
但是list,dist和其他class等,都是浅拷贝的,即仅仅把对象的内存地址拷贝了一份,所有拷贝出来的数据都会共用一组数据。即c里我们常说的指针。
[[0]*3]*2
里的[0]*3确实进行了深拷贝,于是就相当于变成了 [[0 ,0 ,0 ] ]*2。而外层拷贝变成了拷贝list,即浅拷贝,也就是无论拷贝多少行都在使用同一行的数据。
改变1行数据就变成了改变所有行数据。
因此,正确的写法是(当然也是 一行 哦)
l3 = [([0] * 3) for i in range(2)]
print('l3原',l3)
l3[1][1] = 9
print('l3改',l3)
#结果
#l3原 [[0, 0, 0], [0, 0, 0]]
#l3改 [[0, 0, 0], [0, 9, 0]]
是不是帅气而简单呢?
网上还有其他引用其他库的方法去处理。但笔者认为,import最少的库,最高效率,最少代码 去完成是最酷的。至少新手看起来 通俗易懂高大上。