目录:
每篇前言:
🏆🏆作者介绍:【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者
- 🔥🔥本文已收录于Python全栈系列教程专栏:《Python全栈系列教程》
- 🔥🔥热门专栏推荐:《Python全栈系列教程》 | 《爬虫从入门到精通系列教程》 | 《爬虫进阶+实战系列教程》 | 《Scrapy框架从入门到实战》 | 《Flask框架从入门到实战》 | 《Django框架从入门到实战》 | 《Tornado框架从入门到实战》 | 《爬虫必备前端技术栈》
- 🎉🎉订阅专栏后可私聊进一千多人Python全栈交流群(手把手教学,问题解答);进群可领取Python全栈教程视频 + 多得数不过来的计算机书籍:基础、Web、爬虫、数据分析、可视化、机器学习、深度学习、人工智能、算法、面试题等。
- 🚀🚀加入我【博主V信:GuHanZheCoder】一起学习进步,一个人可以走的很快,一群人才能走的更远!
👇 👉 🚔文末扫码关注本人公众号~🚔 👈☝️
首先,我们得了解的是深浅复制究竟是个什么玩意&这玩意到底是干啥的!
打个比方:有糖纸和糖,深复制就相当于糖纸和糖都有,而浅复制就只有糖纸。(这句话牢记于心,在你看完本文后再来反复揣摩本句,如果有很深的心得体会——那么恭喜你:你已经牢牢掌握了python里的深浅复制!)
而我们怎么判断复制前后的对象究竟是不是同一个呢 ?
这就需要用到id(object),它返回的是对象的“身份证号”(在c++中代表在内存中的地址),唯一且不变。
令外,用is判断两个对象是否相等时,依据就是这个id值。
id查看的地址一样,可以说明它们指向的是同一片空间
1. 什么是深浅复制?
在 Python 中,赋值、浅复制(shallow copy)和深复制(deep copy)是三种不同的对象复制方式。
- 赋值(=): 只是创建了新的变量名,并没有创建新的对象,所有变量都指向同一块内存。
- 浅复制(shallow copy): 仅复制对象的第一层,但内层的嵌套对象仍然是共享的。
- 深复制(deep copy): 递归地复制整个对象,包括所有嵌套对象,完全独立于原对象。
举个形象的比喻
假设我们有一个装糖果的糖纸包裹物:
- 赋值(=):只是给糖果包裹物换个名字,并没有真正复制糖果。
- 浅复制:创建了一个新的糖果包裹物,但里面的糖果还是原来的。
- 深复制:既复制了糖果包裹物,也复制了里面的所有糖果,形成一个完全独立的新包装。
2. Python 赋值(=)
在 Python 中,直接使用 =
进行变量赋值时,新变量只是原对象的一个引用,两者共享同一个内存地址。
# 直接赋值
>>> a = [1, 2, 3, 4]
>>> b = a
>>> id(a), id(b) # ID 相同
(1402582080, 1402582080)
修改 a
或 b
,都会影响到对方:
>>> a.append(5)
>>> print(b) # b 也发生了变化
[1, 2, 3, 4, 5]
结论:赋值不会创建新的对象,仅仅是增加一个变量名指向同一块内存。
3. 浅复制(shallow copy)
浅复制创建了一个新的对象,但仅复制第一层,对于嵌套对象,仍然指向原来的内存地址。
>>> import copy
>>> li = [[1, 2], [3, 4]]
>>> li2 = copy.copy(li)
观察 li
和 li2
的 ID:
>>> id(li), id(li2) # li 和 li2 是两个不同的对象
(2011597308424, 2011597308360)
但它们的内层列表仍然是共享的:
>>> id(li[0]), id(li2[0]) # 第一层复制了,内层仍然指向原来的对象
(2011597305032, 2011597305032)
修改第一层不会影响 li2
:
>>> li.append([5, 6])
>>> print(li) # 影响 li
[[1, 2], [3, 4], [5, 6]]
>>> print(li2) # li2 不受影响
[[1, 2], [3, 4]]
但修改内层对象会影响 li2
:
>>> li[0].append(99)
>>> print(li2) # li2[0] 也变了
[[1, 2, 99], [3, 4]]
结论:浅复制只是复制了外层对象,内层对象仍然共享。
4. 深复制(deep copy)
深复制会递归地复制整个对象,保证新对象和原对象完全独立。
>>> li3 = copy.deepcopy(li)
验证 li3
和 li
是完全独立的:
>>> id(li), id(li3) # 两个对象不同
(2011597308424, 2011598772872)
>>> id(li[0]), id(li3[0]) # 内层对象的 ID 也不同
(2011597305032, 2011598772936)
修改 li
任何部分,都不会影响 li3
:
>>> li[0].append(999)
>>> print(li) # li 发生变化
[[1, 2, 99, 999], [3, 4]]
>>> print(li3) # li3 没有变化
[[1, 2, 99], [3, 4]]
结论:深复制会完全独立地复制整个对象及其所有嵌套对象。
5. 深浅复制对比总结
类型 | 是否创建新对象 | 是否独立复制嵌套对象 | 修改原对象是否影响副本 |
---|---|---|---|
赋值(=) | 否 | 否 | 是 |
浅复制 | 是 | 否 | 是(嵌套对象) |
深复制 | 是 | 是 | 否 |
(1)什么时候使用浅复制?
- 当对象本身是简单的一维列表或字典时,可以使用
copy.copy()
。 - 当你只需要复制最外层,而嵌套对象仍然可以共享时。
(2)什么时候使用深复制?
- 当对象有多层嵌套,并且你希望完全独立时,使用
copy.deepcopy()
。 - 当你要避免修改副本时影响原对象。
拓展:Python 内存池机制(小整数池)
Python 维护了一个小整数池,使得小整数在整个程序生命周期内共享相同的内存地址。
>>> a = 1
>>> b = 1
>>> id(a), id(b) # 两者 ID 相同
(1402582080, 1402582080)
但对于较大的整数或对象,Python 不会自动共享内存:
>>> a = 1000
>>> b = 1000
>>> id(a), id(b) # ID 不同
(1402582088, 1402582104)
总结
- 赋值(=) 仅创建新的变量名,不创建新对象。
- 浅复制(copy.copy()) 只复制最外层,嵌套对象仍然共享。
- 深复制(copy.deepcopy()) 递归复制所有层级,完全独立。
- Python 内存池 会优化小整数和字符串的内存使用。
形象的图讲解:
🌟 解决问题,拓展人脉,共同成长!(非诚勿扰)