深拷贝和浅拷贝的区别
赋值
在我们开始讨论浅拷贝和深拷贝的区别之前,首先我们必须清楚什么是赋值?
在Python中,所谓的赋值实际上就是对象引用的传递而已,当创建一个对象的时候,再赋值给另外一个变量的时候,并不是赋值给另一个变量。而是把这个变量在地址空间的id地址值传递给另一个变量,简单的说就是拷贝了这个对象的引用。看图片,可能比较容易理解。
从图中,我们很容易的看到,a和b两个变量在地址空间中是一样。后来,我们又给a增加一个值89之后,a和b两个地址值还是一样。
浅拷贝
浅拷贝是对一个对象父级(外层)的拷贝,并不会拷贝子级(内部)。使用浅拷贝的时候,分为两种情况。
第一种,如果最外层的数据类型是可变的,比如说列表,字典等,浅拷贝会开启新的地址空间去存放。
第二种,如果最外层的数据类型是不可变的,比如元组,字符串等,浅拷贝对象的时候,还是引用对象的地址空间。
- 看图,我们就明白的看出,c的最外层是[a,b],可变数据类型,d在浅拷贝的时候,也直接把最外层拿过来。只拷贝最外层,而里面却没有拿到。
- 1、对于 不可 变类型 Number String Tuple,浅复制仅仅是地址指向,不会开辟新空间。
- 2、对于 可 变类型 List、Dictionary、Set,浅复制会开辟新的空间地址(仅仅是最顶层开辟了新的空间,里层的元素地址还是一样的),进行浅拷贝
- 3、浅拷贝后,改变原始对象中为可变类型的元素的值,会同时影响拷贝对象的;改变原始对象中为不可变类型的元素的值,只有原始类型受影响。 (操作拷贝对象对原始对象的也是同理)
深拷贝
- 1、对于 不可 变类型 Number String Tuple,浅复制仅仅是地址指向,不会开辟新空间。
- 2、对于 可 变类型 List、Dictionary、Set,浅复制会开辟新的空间地址(仅仅是最顶层开辟了新的空间,里层的元素地址还是一样的),进行浅拷贝
- 3、浅拷贝后,改变原始对象中为可变类型的元素的值,会同时影响拷贝对象的;改变原始对象中为不可变类型的元素的值,只有原始类型受影响。 (操作拷贝对象对原始对象的也是同理)
可变类型和不可变类型在浅拷贝中的区别
import copy # 不可变类型 Number String Tuple print("对于不可 变类型 Number String Tuple,浅复制仅仅是地址指向,不会开辟新空间拷贝值") num1 = 17 num2 = copy.copy(num1) print("num1:" + str(id(num1))) print("num2:" + str(id(num1))) # num1和num2的地址都相同 str1 = "hello" str2 = copy.copy(str1) print("str1:" + str(id(str1))) print("str2:" + str(id(str2))) # str1和str2的地址都相同 tup1 = (18, "tom") tup2 = copy.copy(tup1) print("tup1:" + str(id(tup1))) print("tup2:" + str(id(tup2))) # tup1和tup2的地址都相同 print("="*20) print("对于可变类型 List、Dictionary、Set,浅复制会开辟新的空间地址(仅仅是最顶层开辟了新的空间),进行浅拷贝") list1 = [11,12] list2 = copy.copy(list1) print("list1:" + str(id(list1))) print("list2:" + str(id(list2))) # list1和list2的地址不相同 dic1 = [11,12,"hi"] dic2 = copy.copy(dic1) print("dic1:" + str(id(dic1))) print("dic2:" + str(id(dic2))) # dic1和dic2的地址不相同 set1 = {"AA","BB"} set2 = copy.copy(set1) print("set1:" + str(id(set1))) print("set2:" + str(id(set2))) # set1和set2的地址不相同
输出:
对于不可 变类型 Number String Tuple,浅复制仅仅是地址指向,不会开辟新空间拷贝值 num1:4449693616 num2:4449693616 str1:4452098488 str2:4452098488 tup1:4451942472 tup2:4451942472 ==================== 对于可变类型 List、Dictionary、Set,浅复制会开辟新的空间地址,进行浅拷贝 list1:4456844424 list2:4452360136 dic1:4452358856 dic2:4456844744 set1:4452279016 set2:4452279464
对list进浅拷贝,对可变类型和不可变类型修改后的影响。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import copy l1 = [ 11 , 12 ] l2 = [ 21 , 22 ] num = 555 allOne = [l1, l2,num] # 浅拷贝,创建出一个对象,并把旧对象元素的 引用地址 拷贝到新对象当中。 # 也就是说,两个对象里面的元素通过浅拷贝指向的还是同一个地址 allOne2 = copy.copy(allOne) l1[ 0 ] = 16 # 此处修改,会使得 allOne 和 allOne2的第0个元素的值都发生改变,因为l1是List,是可变对象 allOne[ 2 ] = 666 # 此处修改,只会allOne的num的值,因为不可变对象一旦重新复制,地址就会发生改变。(不可变嘛) num = 777 # 此处不会改变 allOne 和 allOne2的值,因为相当于 777 复制给一个全新的地址,这个num跟其他num已经没关系了 print (allOne) print (allOne2) print ( "id allOne:" + str ( id (allOne))) print ( "id allOne[0]:" + str ( id (allOne[ 0 ]))) print ( "id allOne[1]:" + str ( id (allOne[ 1 ]))) print ( "id allOne[2]:" + str ( id (allOne[ 2 ]))) print ( "===" ) print ( "id allOne2:" + str ( id (allOne2))) print ( "id allOne2[0]:" + str ( id (allOne2[ 0 ]))) print ( "id allOne2[1]:" + str ( id (allOne2[ 1 ]))) print ( "id allOne2[2]:" + str ( id (allOne2[ 2 ]))) |
输出
[[16, 12], [21, 22], 666] [[16, 12], [21, 22], 555] id allOne:4467341640 id allOne[0]:4471819912 id allOne[1]:4467342920 id allOne[2]:4466847696 === id allOne2:4471820232 id allOne2[0]:4471819912 id allOne2[1]:4467342920 id allOne2[2]:4466081744
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?