Python深浅拷贝与引用赋值的本质区别及内存原理
文章目录
前言
在Python编程中,你是否遇到过这样的状况:明明新建了一个变量 B,并把变量 A 的值赋给了它,结果修改 B 的时候,A 竟然也莫名其妙地跟着变了? 如果你踩过这个坑,那么恭喜你,你已经触碰到了 Python 中最常让人迷惑的知识点之一:引用赋值、浅拷贝(Shallow Copy)与深拷贝(Deep Copy)。 今天,我们就来扒一扒它们的底裤,把这个知识点彻底嚼碎。
一、为什么会产生拷贝问题?一切皆因“引用”
在
Python中,有一句名言:“万物皆对象”。当你创建一个变量并给它赋值时,你并没有真正把数据“装进”变量里,而是给这个数据贴上了一个标签(引用)。
1.1 直接赋值(赋值的本质是共享)
直接使用 = 号进行赋值,本质上是让多个变量指向了内存中的同一个对象。
# 直接赋值示例
list_a = [1, 2, 3]
list_b = list_a # list_b 只是 list_a 的另一个名字(引用)
list_b.append(4)
print("list_a:", list_a) # 输出: [1, 2, 3, 4]
print("list_b:", list_b) # 输出: [1, 2, 3, 4]
print(id(list_a) == id(list_b)) # 输出: True (内存地址完全一样)
结论: 赋值操作不会开辟新的内存空间,它只是给原有的数据多加了一个别名。修改其中一个,另一个必然跟着变。为了解决这个问题,我们需要真正的“拷贝”。
二、浅拷贝(Shallow Copy):只拷贝“第一层”
浅拷贝会创建一个新的对象,但对于对象内部包含的子对象(比如列表嵌套列表),它只拷贝子对象的引用。
2.1 如何实现浅拷贝?
- 使用内置库:
import copy; b = copy.copy(a) - 使用切片操作(针对列表/元组):
b = a[:] - 使用对象的内置方法:
b = a.copy()(字典、列表可用) - 使用工厂函数:
b = list(a),b = dict(a)
2. 2 浅拷贝的代码演示
我们来看一个包含“嵌套可变对象”的例子:
import copy
# 这是一个嵌套列表
original_list = [1, 2, [3, 4]]
# 进行浅拷贝
shallow_list = copy.copy(original_list)
# 场景 1:修改第一层的不可变元素(不影响原对象)
shallow_list[0] = 'X'
print("修改外层后 original_list:", original_list) # [1, 2, [3, 4]]
print("修改外层后 shallow_list:", shallow_list) # ['X', 2, [3, 4]]
# 场景 2:修改嵌套的内层列表(原对象跟着变了!)
shallow_list[2].append(5)
print("修改内层后 original_list:", original_list) # [1, 2, [3, 4, 5]]
print("修改内层后 shallow_list:", shallow_list) # ['X', 2, [3, 4, 5]]
原理解析: 浅拷贝就像是你建了一个新的大盒子,但大盒子里装的小盒子,依然是原来那个小盒子的“快捷方式”。你往小盒子里塞东西,原对象的小盒子也会多出同样的东西。
三、深拷贝(Deep Copy):彻头彻尾的独立克隆
深拷贝不仅创建一个新对象,还会递归地把对象内部的所有子对象都复制一遍。拷贝完成后,新对象和老对象在内存中完全独立,井水不犯河水。
3.1 如何实现深拷贝?
只能通过引入 copy 模块来实现:import copy; b = copy.deepcopy(a)
3.2 深拷贝的代码演示
同样是嵌套列表,我们看看深拷贝的表现:
import copy
original_list = [1, 2, [3, 4]]
# 进行深拷贝
deep_list = copy.deepcopy(original_list)
# 修改嵌套的内层列表
deep_list[2].append(5)
print("修改内层后 original_list:", original_list) # 输出: [1, 2, [3, 4]] (原列表安然无恙!)
print("修改内层后 deep_list:", deep_list) # 输出: [1, 2, [3, 4, 5]]
原理解析: 深拷贝会把大盒子、小盒子以及里面的所有东西,全部重新打造一份一模一样的。无论你怎么折腾新对象,都不会影响到原对象。
四、 核心总结与对比
为了方便记忆,我们可以用一张表来总结直接赋值、浅拷贝和深拷贝的区别:
| 操作类型 | 语法示例 | 内存地址(外层) | 内存地址(内层可变对象) | 是否相互影响? |
|---|---|---|---|---|
| 直接赋值 | b = a |
相同 | 相同 | 牵一发而动全身,完全互相影响。 |
| 浅拷贝 | b = copy.copy(a) 或 b = a[:] |
不同 | 相同 | 修改外层互不影响;修改内层嵌套对象,互相影响。 |
| 深拷贝 | b = copy.deepcopy(a) |
不同 | 不同 | 完全独立,彻底互不影响。 |
特别注意:关于不可变对象
如果你的对象里全部都是不可变元素(如数字、字符串、元组 (1, 2, 3)),那么浅拷贝和深拷贝的表现几乎是一样的。深拷贝和浅拷贝的真正区别,只有在面对可变对象(如列表、字典、集合)且存在多层嵌套时,才会显现出来。
结语
在日常的 Python 开发中:
- 如果数据结构很简单(只有一层),直接使用
.copy()浅拷贝即可,效率更高。 - 如果在处理复杂的配置文件、多维数组或复杂的嵌套字典,且需要对其进行修改而不影响原数据,请务必果断使用
copy.deepcopy()。
更多推荐
所有评论(0)