首页 > 代码库 > python list的深拷贝与浅拷贝-以及初始化空白list的方法(1)
python list的深拷贝与浅拷贝-以及初始化空白list的方法(1)
在一次做题的时候遇到了一件令人非常匪夷所思的“bug”:我想要做的事情是,初始化shape确定,但值为“空”的list,并且是一个二维的list,开始我是这么做的:
l1=[[""]*3]*3 print l1 [[‘‘, ‘‘, ‘‘], [‘‘, ‘‘, ‘‘], [‘‘, ‘‘, ‘‘]]
可以看到这个矩阵的shape,可以理解成一个N*N的矩阵。
把题目也贴出来吧——题目要求很简单,输入一个N*N的矩阵,输出这个矩阵的顺时针旋转90°之后的矩阵。一开始我的代码如下:
class Rotate: def rotateMatrix(self, mat, n): # write code here v=[[""]*3]*3 for i in range(n): for j in range(n): v[i][j]=mat[n-j-1][i] return v
输入的测试数据为:
[[1,2,3],[4,5,6],[7,8,9]]
正确的输出应该为:
[[7,4,1],[8,5,2],[9,6,3]]
而我的运行结果却是:
[[9,6,3],[9,6,3],[9,6,3]]
非常不解,输出的三个子数组居然是一样的结果
后来对原来的代码略加修改,把“空”list的初始化定义为:v=[[
0
for
i in range(n)]
for
j in range(n)],即:
class Rotate: def rotateMatrix(self, mat, n): # write code here v=[[0 for i in range(n)] for j in range(n)] for i in range(n): for j in range(n): v[i][j]=mat[n-j-1][i] return v
此时终于输出了正确的结果[[9,6,3],[9,6,3],[9,6,3]],代码测试通过。
这里对比一下这两种初始化方法:
至少在输出结果看,长的是一模一样,难道这里的l1和l2真的没有区别吗?这里再介绍一个常用函数------id(x),它表示括号内变量所在的内存地址
我们分别来看一下l1,l2内子list的内存地址情况:
震惊!这里可以看到,l1的三个子list的内存地址居然一模一样!而l2的子list所在的内存地址各不相同!??
我们队l1,l2的子list分别进行extend操作,如下:
我们本来只是对l1/l2的第二个子list进行extend操作,但结果却是l1的三个子数组都受到了影响,而l2是只有第二个子数组实现了extend操作。
再分别看此时各子list的内存地址:
与刚才的结果一致,l1的三个子数组位于同一段内存地址,l2则各不相同。
用个直接使用“=”对l1的子list进行赋值呢?
此时,只有被赋值的l1[1]成功赋值,另外两个没有变,并且l1[1]的内存地址已经发生变化,而l1[0],l1[2]依然不变
至此可以总结一下原因了:
1.使用*进行list“复制”,其生成的新数组与被复制的数组其实是在同一段内存地址当中,这样的复制方式成为潜复制
2.浅复制进行初始化的结果就是,在对复制之后的对象进行相关操作时,被复制的对象会受到同样的影响,因为他们本质是同一段list,均位于相同的地址
3.使用“=”进行赋值,是将"="后的内容创建到一个新的内存地址当中,并将"="左的变量的地址重新指向"="右产生的新地址当中
具体到解决实际问题,在初始化一个"已知shape,但内部的值待填充"的一个"空"list时,建议使用for循环进行初始化,例如上文提到这个格式,即:
v=[[‘‘ for i in range(m)] for j in range(n)]
python list的深拷贝与浅拷贝-以及初始化空白list的方法(1)