python深拷贝和浅拷贝
正文:
- 不可变对象和可变对象
- 浅拷贝
- 深拷贝
Python 内 不可变对象 的内存管理方式是引用计数。
因此,我们在谈论拷贝时,其实谈论的主要特点都是基于 可变对象的。
操作不可变对象示例
import copy a = "文字" b = a # 赋值 c = copy.copy(a) # 浅拷贝 d = copy.deepcopy(a) # 深拷贝 print(id(a)) print(id(b)) print(id(c)) print(id(d))
输出结果:
1626112499920 1626112499920 1626112499920 1626112499920
由于操作的是不可变对象,Python 用引用计数的方式管理它们,所以 Python 不会对值相同的不可变对象,申请单独的内存空间。只会记录它的引用次数
浅拷贝
浅拷贝是对于一个对象的顶层拷贝,通俗的理解是:拷贝了引用,并没有拷贝内容。
浅拷贝和赋值在 可变对象 上的区别
示例1
import copy a = ["九少"] b = a c = copy.copy(a)
print(id(a))
print(id(b))
print(id(c))
# 输出结果
2288367604672
2288367604672
2288367604608 # 浅拷贝会创建一个新的对象
赋值就是对物体进行贴标签操作,作用于同一物体。而浅拷贝则会创建一个新的对象,至于对象中的元素,它依然会引用原来的物体,我们再来看一段例子
示例2
import copy a = ["九少"] print(id(a))
print(id(a[0])) c = copy.copy(a) print(id(c))
print(id(c[0])) a[0] = "天才" print(id(a))
print(id(a[0]))
print(id(c))
print(id(c[0]))
# 输出结果
1541051814848 # a
1541048082640 # a[0]
1541051814784 # c
1541048082640 # c[0]
1541051814848 # a的位置没变
1541048082736 # a里面的元素a[0]变了
1541051814784 # c没变
1541048082640 # c里面的元素c[0]没变
操作不可变对象 a[0]='九少' 时,由于引用计数的特性,被拷贝的元素 a[0]='九少' 改变时,就相当于撕掉了原来的标签,重新贴上新的标签一样,对于我们已拷贝的元素没有任何影响。因此在操作不可变对象时,浅拷贝和深拷贝是没有区别的
示例3
import copy import json a = [["九少"], "天才"] print(json.dumps(a, ensure_ascii=False)) print(id(a[0]),id(a[0][0]),id(a[1])) c = copy.copy(a) print(json.dumps(c, ensure_ascii=False)) print(id(c[0]),id(c[0][0]),id(c[1])) a[0][0] = "张三" a[1] = "李四" print(json.dumps(a, ensure_ascii=False))
print(id(a[0]),id(a[0][0]),id(a[1]))
print(json.dumps(c, ensure_ascii=False))
print(id(c[0]),id(c[0][0]),id(c[1]))
# 输出结果
[["九少"], "天才"] # a
1990591288320 1990587556048 1990587556144 # a里面的元素
[["九少"], "天才"] # c
1990591288320 1990587556048 1990587556144 # c里面的元素位置和a里面元素位置一样
[["张三"], "李四"] # 改变后的a
1990591288320 1990590757168 1990590757072 # a[0][0]和a[1]发生了改变
[["张三"], "天才"] # 改变后的c
1990591288320 1990590757168 1990587556144 # c[0][0] 发生了改变
由于浅拷贝会使用原始元素 '九少' 的引用(内存地址)。所以在在操作被拷贝对象内部的可变元素 ['九少'] 时,其结果是会影响到拷贝对象的
深拷贝
深拷贝遇到可变对象,则又会进行一层对象创建,所以你操作被拷贝对象内部的可变对象,不影响拷贝对象内部的值
import copy import json a = [["九少"], "天才"] print(json.dumps(a, ensure_ascii=False)) print(id(a[0]),id(a[0][0]),id(a[1])) c = copy.deepcopy(a) print(json.dumps(c, ensure_ascii=False)) print(id(c[0]),id(c[0][0]),id(c[1])) a[0][0] = "张三" a[1] = "李四" print(json.dumps(a, ensure_ascii=False)) print(id(a[0]),id(a[0][0]),id(a[1])) print(json.dumps(c, ensure_ascii=False)) print(id(c[0]),id(c[0][0]),id(c[1])) # 输出结果 [["九少"], "天才"] # a 2073609671744 2073606398160 2073606398256 [["九少"], "天才"] # c 2073609671104 2073606398160 2073606398256 [["张三"], "李四"] # 改变后的a 2073609671744 2073609140528 2073609140432 [["九少"], "天才"] # 改变后的c 2073609671104 2073606398160 2073606398256 # c无地址发生变化
总结
1,对于不可变对象,浅拷贝和深拷贝的作用是一致的,就相当于复制了一份副本,原对象内部的不可变对象的改变,不会影响到复制对象
2,浅拷贝的拷贝。其实是拷贝了原始元素的引用(内存地址),即浅拷贝对于对象内部的可变对象,仅仅是指向,而不是创建新的副本,所以当拷贝可变对象时,若改变原对象内可变对象的对应元素,会影响在拷贝对象的对应元素,
3,深拷贝在遇到可变对象时,又在内部做了新建了一个副本。所以不管深拷贝内部的元素如何变化,都不会影响到原来副本的可变对象
这个讲的不错:https://www.bilibili.com/video/BV1cc411h7hd/?spm_id_from=333.337.search-card.all.click&vd_source=8bc93faaf8c822f06e3c15f0c4d2e6d3
参考学习:https://zhuanlan.zhihu.com/p/57893374
https://www.cnblogs.com/eczhou/p/7860668.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」