Python中浅拷贝和深拷贝的理解
浅拷贝(Shallow Copy)
定义
创建一个新对象,但仅复制原对象的顶层元素引用(不递归复制嵌套的可变子对象)。
特点
顶层独立:新对象和原对象的容器本身是独立的,修改顶层元素(如替换列表中的某个元素)不会相互影响。
子对象共享:嵌套的可变子对象(如列表中的子列表、字典中的子字典)仍被共享,修改子对象内容会影响所有引用它的对象。
详解
如果不理解以上顶层元素和嵌套可变子对象的含义,可以看看下面的解释:
在浅拷贝中,对于原始列表中的每一个元素,浅拷贝会创建一个新的列表,然后将原始列表中每个元素的引用复制到新列表中。
但是,这里要注意元素的性质:
- 如果元素是不可变对象(如整数、字符串、元组等),那么即使共享引用也没有关系,因为不可变对象不能修改,所以修改操作会创建新对象,不会影响其他引用。
- 如果元素是可变对象(如列表、字典等),那么浅拷贝后,新列表和原列表中对应位置仍然引用同一个可变对象。因此,通过一个列表修改这个可变对象,另一个列表也会看到变化。
所以,浅拷贝后的两个对象(原列表和新列表)本身是两个不同的对象(即它们的id不同),但是它们内部的元素(特别是可变元素)是共享的(即相同的引用)。
举个例子:
original_list = [1, 2, [3, 4]]
copied_list = copy.copy(original_list)
这时:
- original_list 和 copied_list 是两个不同的列表对象(id不同)。
- 对于前两个不可变元素(整数1和2),在两个列表中是独立的吗?实际上,它们共享相同的引用,但因为整数是不可变的,所以修改其中一个列表的顶层元素(比如将第一个元素改为5)不会影响另一个。
- 但是,对于第三个元素,它是一个列表(可变对象),所以两个列表的第三个元素都指向同一个列表对象。因此,如果通过其中一个列表修改这个嵌套列表(例如:original_list[2][0] = 100),那么另一个列表也会看到这个改变。
注意:修改顶层元素和修改嵌套元素的不同:
- 如果执行 copied_list[0] = 10,这只会改变copied_list的第一个元素,而original_list的第一个元素还是1。这是因为列表的顶层元素是独立的(每个位置存储的是引用,但是赋值操作只是改变copied_list的第一个引用,不会影响original_list)。
- 但是,如果执行 copied_list[2][0] = 100,那么由于copied_list[2]和original_list[2]引用的是同一个列表,所以这个修改会同时反映在两个列表中。
因此,我们可以这样总结浅拷贝:
- 创建一个新列表(新对象)。
- 将原列表中的每一个元素的引用(内存地址)复制到新列表中。
- 因此,新列表和原列表在内存中是两个独立的列表,但是它们内部的元素(尤其是可变元素)是共享的。
所以,对于顶层元素(即直接包含在列表中的元素):
- 如果它们是可变对象,那么修改这些可变对象的内容(而不是替换整个对象)会影响到另一个列表。
- 如果替换顶层元素(比如给某个索引重新赋值一个新对象),那么只是改变了一个列表的该索引处的引用,另一个列表不变。
适用场景
- 对象结构简单(无嵌套可变对象)
- 需要高效复制且允许共享子对象时
这样一解释深拷贝就显得容易理解多了吧↓
深拷贝 (Deep Copy)
定义
创建一个新对象,并递归复制原对象及其所有嵌套子对象,完全独立于原对象。
特点
- 完全独立:新对象与原对象及其所有子对象均无引用共享,任何修改互不影响。
- 性能开销:因需递归复制所有层级,比浅拷贝更耗时耗内存。
实现方式
import copy
new_list = copy.deepcopy(original_list) # 深拷贝
适用场景
- 对象结构复杂(含多层嵌套可变对象)
- 需要完全独立的副本时