Python中的深拷贝与浅拷贝
目录
1. 概念简介
1.1 浅拷贝(shallow copy)
1.2 深拷贝(deep copy)
3. 不同类型拷贝行为总结
4. 元组中包含可变对象的特殊情况
5. 何时使用浅拷贝或深拷贝?
6. 重要提醒
1. 概念简介
1.1 浅拷贝(shallow copy)
- 定义:复制最外层对象,但内部元素(子对象)仍然是原对象中元素的引用(地址共享)。
- 表现:
- 不可变对象(如整数、字符串、元组等)浅拷贝不会创建新对象,拷贝后对象和原对象指向同一个内存地址(id相同)。
- 可变对象(如列表、字典、集合等)浅拷贝会创建一个新的最外层对象(id不同),但内部元素的引用是共享的(id相同)。
- 代码示例:
import copy# 不可变对象:整数列表中的元组
immutable_obj = (1, 2, 3)
immutable_obj_shallow = copy.copy(immutable_obj)
print(f"id(immutable_obj): {id(immutable_obj)}")
print(f"id(immutable_obj_shallow): {id(immutable_obj_shallow)}") # id相同# 可变对象:列表包含列表
mutable_obj = [1, 2, [3, 4]]
mutable_obj_shallow = copy.copy(mutable_obj)
print(f"id(mutable_obj): {id(mutable_obj)}")
print(f"id(mutable_obj_shallow): {id(mutable_obj_shallow)}") # id不同,外层对象新建print(f"id(mutable_obj[2]): {id(mutable_obj[2])}")
print(f"id(mutable_obj_shallow[2]): {id(mutable_obj_shallow[2])}") # 内部列表引用相同
解析:
- 对于不可变元组,浅拷贝后
id
相同,说明没有创建新对象。 - 对于可变列表,浅拷贝创建了新的外层列表(id不同),但内部的子列表仍然是共享的(id相同),修改内部子列表会影响原对象。
1.2 深拷贝(deep copy)
- 定义:递归复制最外层对象及其所有子对象,生成一个完全独立的新对象。
- 表现:
- 不论可变或不可变,深拷贝都会创建新的最外层对象(但对纯不可变对象,通常复用内存不创建新对象)。
- 对于可变子对象,深拷贝会递归复制,子对象的id与原对象不同,完全独立。
- 对于不可变子对象,Python通常复用内存,不会重复创建。
- 代码示例:
import copy# 不可变对象:简单元组
immutable_obj = (1, 2, 3)
immutable_obj_deep = copy.deepcopy(immutable_obj)
print(f"id(immutable_obj): {id(immutable_obj)}")
print(f"id(immutable_obj_deep): {id(immutable_obj_deep)}") # id相同,未创建新对象# 可变对象:列表包含列表
mutable_obj = [1, 2, [3, 4]]
mutable_obj_deep = copy.deepcopy(mutable_obj)
print(f"id(mutable_obj): {id(mutable_obj)}")
print(f"id(mutable_obj_deep): {id(mutable_obj_deep)}") # id不同,外层对象新建print(f"id(mutable_obj[2]): {id(mutable_obj[2])}")
print(f"id(mutable_obj_deep[2]): {id(mutable_obj_deep[2])}") # 内部列表id不同,递归复制
解析:
- 对于不可变元组,深拷贝返回原对象本身,没有创建新对象,
id
相同。 - 对于可变列表,深拷贝递归复制了所有子对象,包括内层列表,所有对应对象的
id
都不同,完全独立。
3. 不同类型拷贝行为总结
类型 | 赋值 | 浅拷贝 | 深拷贝 |
---|---|---|---|
不可变对象 | 引用,id相同 | 不创建新对象,id相同 | 不创建新对象,id相同 |
可变对象 | 引用,id相同 | 新建外层对象,内部引用共享 | 递归新建所有对象,完全独立 |
元组(不可变) | 引用,id相同 | 新建元组对象,内部元素引用共享 | 新建元组对象,内部元素递归复制 |
4. 元组中包含可变对象的特殊情况
- 元组本身不可变,浅拷贝会创建新的元组对象(id不同)。
- 元组内的可变元素(如列表)浅拷贝时不会复制,仍然是共享引用(id相同)。
- 深拷贝时,元组和其中所有子对象都会被递归复制,子对象id也不同。
5. 何时使用浅拷贝或深拷贝?
情况 | 选择理由 | 选择拷贝类型 |
---|---|---|
对象结构简单,无嵌套或子对象不可变 | 性能开销小,快速复制 | 浅拷贝 |
对象包含可变的嵌套子对象,需独立修改 | 需要完全独立的对象 | 深拷贝 |
只需复制最外层对象,内部共享子对象 | 节省内存,避免重复复制 | 浅拷贝 |
复杂数据结构备份或递归操作 | 防止意外修改原始数据 | 深拷贝 |
6. 重要提醒
- 不可变对象的浅拷贝和深拷贝效果相同,Python会复用内存。
- 浅拷贝不会递归复制内部子对象,修改嵌套的可变子对象会影响原对象。
- 深拷贝性能开销较大,不必要时避免使用。