今天同学们自建的一个python学习群发来一个问题,他想创建一个二维列表并且做如下赋值,代码如下:
a = [['']*3] * 3
for m in range(1, 4):
for n in range(1, 4):
a[m-1][n-1] = str(m * n)
print('a[' + str(m-1) + '][' + str(n-1) + '] = ' + a[m-1][n-1], end=' ')
print()
print(type(a))
print(a)
-----------------------------------------------------------------------------------
预想的结果这个二维列列表应该是[[‘1’,‘2’,‘3’],[‘2’,‘4’,‘6’],[‘3’,‘6’,‘9’]]
然而运行结果如下
a[0][0] = 1 a[0][1] = 2 a[0][2] = 3
a[1][0] = 2 a[1][1] = 4 a[1][2] = 6
a[2][0] = 3 a[2][1] = 6 a[2][2] = 9
<class 'list'>
[['3', '6', '9'], ['3', '6', '9'], ['3', '6', '9']]
于是我在赋值语句设置断点,看看各个变量的变化情况,发现每次赋值,对应同一列的元素都会发生改变。
于是我上网查了查“python如何创建二维列表“,得知原来是深浅copy的原因。
浅copy:copy后的变量外壳的内存地址不同,但是变量内部元素的内存地址相同
举例:
lst1 = [1, 2, [3, 4]]
lst2 = lst1.copy()
# 对列表进行添加操作
lst1.append(5)
print('-----------第一处对比-----------')
print(lst1)
print(lst2) # 从结果可以看到,lst1改变而lst2没有改变
# 对其中的列表元素进行添加操作
lst1[2].append('变')
print('-----------第二处对比-----------')
print(lst1)
print(lst2) # 从结果可以看到,lst1和lst2都发生了改变
# 查看他们的ID
print('lst1的id:', id(lst1)) # lst1 与 lst2 的内存地址是不同的
print('lst1的id:', id(lst2))
print('lst1第0个元素的id:', id(lst1[0])) # 但是其中的元素的地址是相同的
print('lst2第0个元素的id:', id(lst2[0]))
运行结果:
所以,开篇问题代码中a = [[' ']*3]*3 这条语句中的第二个* 就类似于这里的浅copy,这么说可能不太准确,但是其意思就是这样得出的列表中的三个列表元素指向的内存地址是一样,也就是这三个列表元素实质是同一个列表,只是可以通过不同的索引得到它们。所以这就解释了为什么每次赋值的时候同一列的元素都会发生改变。
这里提供一个正确的二维列表的创建方式:
a = [[ 0 for i range(3)] for j in range(3)] # 创建一个3x3的所有元素都为0的二维列表
这样创建的二维列表不会存在浅copy的问题。
深Copy:copy后的变量外壳的内存地址不同,内部元素可变数据类型的地址不同,不可变数据类型的地址相同。