一、数据类型
数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型。
基本数据类型的特点:直接存储在栈(stack)中的数据
引用数据类型的特点:存储的是该对象在栈中引用,真实的数据存放在堆内存里
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
image.png二、浅拷贝与深拷贝
2.1 概述:
浅拷贝可以使用列表自带的copy()函数(如list.copy()),或者使用copy模块的copy()函数。深拷贝只能使用copy模块的deepcopy(),所以使用前要导入:from copy import deepcopy
深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。
如果拷贝的对象里的元素只有值,没有引用,那浅拷贝和深拷贝没有差别,都会将原有对象复制一份,产生一个新对象,对新对象里的值进行修改不会影响原有对象,新对象和原对象完全分离开。
如果拷贝的对象里的元素包含引用(像一个列表里储存着另一个列表,存的就是另一个列表的引用),那浅拷贝和深拷贝是不同的,浅拷贝虽然将原有对象复制一份,但是依然保存的是引用,所以对新对象里的引用里的值进行修改,依然会改变原对象里的列表的值,新对象和原对象并没有完全分离开。而深拷贝则不同,它会将原对象里的引用也新创建一个,即新建一个列表,然后放的是新列表的引用,这样就可以将新对象和原对象完全分离开。
2.2 实现原理
具体过程详见以下连接:https://blog.csdn.net/qq_34493908/article/details/81560546
【1】浅拷贝:
aa=[1,2,3,[4,5]]
bb=copy(aa) # 使用copy()函数对aa进行拷贝赋值给bb
bb[3][0]=9 #修改bb列表里[4,5]中的4为9
对于bb=copy(aa)的操作,其在内存当中的示意图如下:
image.png
因此,当我们执行到bb[3][0]=9这块代码时:其在内存当中实际操作如下:
显然,bb里的值改变了,aa里的值也改变了,即证实:
浅拷贝虽然将原有对象复制一份,但是依然保存的是引用,所以对新对象里的引用里的值进行修改,依然会改变原对象里的列表的值,新对象和原对象并没有完全分离开。
【2】深拷贝
aa=[1,2,3,[4,5]]
bb=deepcopy(aa) #使用deepcopy()函数对aa进行深拷贝赋值给bb
bb[3][0]=9 #修改bb列表里[4,5]中的4为9
对于bb=deepcopy(aa)的操作,其在内存当中的示意图如下:
因此,当我们执行到bb[3][0]=9这块代码时:其在内存当中实际操作如下:
image.png
显然:很明显只改变bb列表里列表[4,5]中的4为9,aa中的没有改变,因为它跟原aa列表指向的引用是不同的。这也充分验证了这句话:
而深拷贝则不同,它会将原对象里的引用也新创建一个,即新建一个列表,然后放的是新列表的引用,这样就可以将新对象和原对象完全分离开
四、深拷贝和浅拷贝的示意图:
image.png浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
五、赋值和浅拷贝的区别
当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。
网友评论