深拷贝,浅拷贝就如何理解

问题描述

学员询问为何明明使用的是浅拷贝,但是二者的内存地址却是不一样

a= [1,2,3,[1,2,3]]
b== copy.copy(a)
#但是 id(a)!=id(b)

深拷贝浅拷贝简略讲解

可变内存地址,和不可变内存地址

a= [1,2,3,[a,v,c]]
b=a #这个赋值操作实际上是一种浅拷贝
#这时a列表的内存地址:id(a) 其实返回的值是对于[1,2,3,[a,v,c]]这个可变变量的内存地址
即
id(b)!=id(a)
但是
如果我们把a的列表中的数据修改一下:
1. a=[1,2,3,[a,b,c]]  b也会变为: a=[1,2,3,[a,b,c]]
2. a=[2,2,3,[a,v,c]]  b:a=[1,2,3,[a,v,c]]
浅拷贝实际上也复制内存空间,但因为是浅拷贝,所以只拷贝最浅层的地址,但是为何id(a)和id(b)的值不同,就表明他们的一级指向的内存地址不一样。但是他们在修改时又有时会同步,有时不同步

#解析:
无论是赋值语句=,还是浅拷贝 copy.copy(),在面对不可变数据时和面对可变数据时的操作时不一样的。
如果 a=1 ,b=a  那么id(a)==id(b),这是因为对于变量a的内存空间是指定的,即其值存放在一个内存地址。但是对于 a=[1,2,3,[4,5,22]]来说列表是一个可变变量,其内存地址是不固定的,怎么说呢,
实际上这个内存地址里存放的不是数据本身,而是指向数据的内存地址位置:
#对于列表而言
id(列表)里面放的不是数据,而是数据的位置,当然了也不全是,比如列表里的不可变变量:1,2,3这类的东西,放的则是数据本身,然后对于列表里的[4,5,22],存放的则是这个列表的内存地址,然后这个列表里存放的又是一层嵌套了。

然后如何解释为啥id(a)和id(b)不一致,因为对于浅拷贝来说,如果是拷贝可变变量,那么并没有一个统一的盒子,即最外层的盒子是是需要系统新建一个的,比如说a= [1,2,3] 相当于有个盒子A,里面装着三个地址:1:存放数据的地址,2:存放数据的地址,3:存放数据的地址(什么叫存放数据的地址,就是这个地址里放的是终点,而不是下一关),然后 对于b来说,浅拷贝,并不会直接复制盒子A,而是只是复制盒子A里的东西,即会新建一个盒子B,里面装着同样的三个地址:1,2,3。
然后这三个地址指向的是各自的终点房
那么如此,便解释了为何id(a)和id(b)不一样,因为不是同一个盒子

然后我们来解释为何修改列表:a=[1,2,3,[4,5,6]]->[2,2,3,[4,5,6]]数据却没有同步修改。
因为当我们做出上述修改后,因为修改的是不可变量,所以所作的操作并非将1的终点房里的东西进行替换,而是替换存放在列表里的指路地址,将其替换为指向不可变变量2的地址。但是这种替换会发生在列表b中吗?很明显不会,因为同步修改的本质并非是因为在对a做出修改时,对b也做出同步修改,(而是对他们当前地址指向的更深层的关联) 额,这个不好说,我们就先放到下面来说:

刚刚我们已经讨论过了,为啥替换不可变量1为2时,为何b不会同步修改。
因为a做出的修改是替换当层的指向路径。
原本来说,浅拷贝后,他们的路径指向为:
A->
     x1->1
     x2->2
     x3->3
B->
但是在修改后:
     X2->2
A->  X2->2 
     X3->3

因为我们知道B里面存放的东西没有发生变化
    X1->1
B-> X2->2
    X3->3
这时,二者的值自然就不一样,B自然没有被"同步修改"
这里解释了为啥修改[1,2,3]->[2,2,3]没有同步修改
# 接下来就是为啥这样又会同步修改:[1,2,3,[1,2,3]]->[1,2,3,[2,2,3]]

   X1->1
A->X2->2
   X3->3
          X1->1
   Xn1->  X2->2
          X3->3
    
   X1->1
B->X2->2
   X3->3
          X1->1
   Xn1->  X2->2
          X3->3
    
    
然后修改A [1,2,3,[1,2,3]]->[1,2,3,[2,2,3]]

   X1->1
A->X2->2
   X3->3
          X2->2
   Xn1->  X2->2
          X3->3

对于B来说
   X1->1
B->X2->2
   X3->3
          X2->1
   Xn1->  X2->2
          X3->3
为社么这次B却能一样修改,就是因为对于A来说是修改了xn1里放的值,对于B来说,它不关心xn1里有什么,它只知道它有个去xn1的地址,那么它最终也会读取xn1里的数据,这时如果你修改了xn1里放的值,则当B前往Xn1拿数据时就会拿到Xn1目前的值。

这就是为何数据会同步修改。