深拷贝浅拷贝是个很容易迷糊的问题,本人帮你彻底搞清楚。
粗识内存
本人没学过c,内存略懂,有堆栈之分,
栈可以理解为程序自动分配的内存,堆可以理解为程序员对内存的引用,不重要,有感觉就行。
浅拷贝
浅拷贝并不是我们认知中的“复制”,浅拷贝只是对象的引用,是对一个对象的浅层拷贝,所以叫浅拷贝
或者说是顶层拷贝,只拷贝了引用,没拷贝内容
示例代码
import copy a = [1, 2, [3, 4]] b = copy.copy(a) print('b:', b) # ('b:', [1, 2, [3, 4]]) b.append(5) b[2].append(6) print('a:', a) # ('a:', [1, 2, [3, 4, 6]]) print('b:', b) # ('b:', [1, 2, [3, 4, 6], 5])
为什么这样呢?
b是a的浅拷贝,对b的操作不会影响a,但是对b内元素操作会影响a,因为ab指向的是同一个元素
b只拷贝了a的第一层,所以叫浅拷贝
print(id(a)) # 40641880 print(id(b)) # 40667616 b新建了一个对象 print(id(a[2])) # 40643080 print(id(b[2])) # 40643080 b中元素仍是a中元素
深拷贝
深拷贝才是我们认知中的“复制”,深拷贝是完全拷贝,是对一个对象的深层拷贝,所以叫深拷贝。
示例代码
import copy a = [1, 2, [3, 4]] b = copy.deepcopy(a) print('b:', b) # ('b:', [1, 2, [3, 4]]) b.append(5) b[2].append(6) print('a:', a) # ('a:', [1, 2, [3, 4]]) print('b:', b) # ('b:', [1, 2, [3, 4, 6], 5])
好理解了
互不影响
b拷贝了a的所有层,所以叫深拷贝
print(id(a)) # 41624920 print(id(b)) # 41648496 b新建了一个对象 print(id(a[2])) # 41626120 print(id(b[2])) # 41648456 b中元素不是a中元素
可以看到深拷贝和浅拷贝都新建了一个对象。
等于
那等于是什么?深拷贝?浅拷贝?都不是,等于才是真正的复制。
示例代码
a = [1, 2, [3, 4]] b = a print('b:', b) # ('b:', [1, 2, [3, 4]]) b.append(5) b[2].append(6) print('a:', a) # ('a:', [1, 2, [3, 4, 6], 5]) print('b:', b) # ('b:', [1, 2, [3, 4, 6], 5]) print(id(a)) # 40510648 print(id(b)) # 40510648 b没有新建对象 print(id(a[2])) # 40490568 print(id(b[2])) # 40490568 b中元素仍是a中元素
互相影响,因为是同一个对象
所以 复制 != 拷贝
好了,终于明白了,真的吗?
看个实例
jack = ['jack', ['age', 20]] tom = jack[:] anny = list(jack) print id(jack), id(tom), id(anny) # 144846988 144977164 144977388
3个不同的对象
tom[0] = 'tom' anny[0] = 'anny' print jack, tom, anny # ['jack', ['age', 20]] ['tom', ['age', 20]] ['anny', ['age', 20]]
可以理解
anny[1][1] = 18 print jack, tom, anny # ['jack', ['age', 18]] ['tom', ['age', 18]] ['anny', ['age', 18]]
请问,还理解吗?
分析:上面是新建了3个对象,那么只能是深拷贝或浅拷贝中的一种,改了名字互不影响,应该是深拷贝,但是改了年龄互相影响,又不是深拷贝,那是浅拷贝?也不对啊,迷糊了
[id(x) for x in jack] # [3073896320L, 3073777580L] [id(x) for x in tom] # [144870744, 3073777580L] [id(x) for x in anny] # [144977344, 3073777580L]
可以看到名字id不同,年龄id相同
为什么会这样呢?
因为python中字符串是不可变类型,其被修改时,必须新建一个对象,这就明白了,就是浅拷贝,年龄指向了同一个id,但是因为名字修改时重新创建了对象, 所以指向了不同的id
那我们在上面浅拷贝的例子上验证下
b[2] = 3 print('a:', a) # ('a:', [1, 2, [3, 4, 6]]) print('b:', b) # ('b:', [1, 2, 3, 5])
果然改变了b[2],新建了个对象,a[2]并没有改变
如果用深拷贝就没有上面的问题
jack = ['jack', ['age', '20']] import copy tom = copy.deepcopy(jack) anny = copy.deepcopy(jack) # 根据第一个思路进行重命名,重定岁数操作: tom[0] = 'tom' anny[0] = 'anny' print jack, tom, anny # ['jack', ['age', '20']] ['tom', ['age', '20']] ['anny', ['age', '20']] anny[1][1] = 18 print jack, tom, anny # ['jack', ['age', '20']] ['tom', ['age', '20']] ['anny', ['age', 18]]
总结
切片操作和工厂方法list方法拷贝是浅拷贝,而且一般情况下都是浅拷贝
深拷贝浅拷贝需要考虑数据是否是可变类型
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人