深拷贝与浅拷贝
说在前面-关于拷贝的魔法函数:
-
__copy__
(self):定义对类的实例使用 copy.copy() 时的行为。copy.copy() 返回一个对象的浅拷贝,这意味着拷贝出的实例是全新的,然而里面的数据全都是引用的。也就是说,对象本身是拷贝的,但是它的数据还是引用的(所以浅拷贝中的数据更改会影响原对象)。 -
__deepcopy__
(self, memodict=)
定义对类的实例使用 copy.deepcopy() 时的行为。copy.deepcopy() 返回一个对象的深拷贝,这个对象和它的数据全都被拷贝了一份。memodict 是一个先前拷贝对象的缓存,它优化了拷贝过程,而且可以防止拷贝递归数据结构时产生无限递归。当你想深拷贝一个单独的属性时,在那个属性上调用 copy.deepcopy() ,使用 memodict 作为第一个参数。
1、什么是拷贝
- 拷贝是指使用一个已存在一个对象,生成一个新的对象,两个对象在内存中具有独立的存储空间。
2、浅拷贝
- 浅拷贝是指是创建一个新的对象时,只拷贝内容是原始对象的引用,而不是创建原始对象的副本数据。
- 浅拷贝不具有数据独立性,对象的
copy()
方法,copy
模块的copy()
方法,工厂方法,切片等方式得到的都是浅拷贝对象。- 使用对象的copy()方法得到浅拷贝对象;
- 使用工厂方法获取浅拷贝对象;
- 使用切片方式获取浅拷贝对象;
- 使用 copy模块中的copy方法获取浅拷贝对象;
import copy
# 原始数据
originData = [[1,2],{"name":"Tom", "chars":["A","B"]}]
# 使用对象的copy()方法得到浅拷贝对象
copyData1 = originData.copy()
# 使用工厂方法获取浅拷贝对象
copyData2 = list(originData)
# 使用切片方式获取浅拷贝对象
copyData3 = originData[:]
# 使用 copy模块中的copy方法获取浅拷贝对象
copyData4 = copy.copy(originData)
# 拷贝成功的验证,内容相同,地址不同
# 查看所有对象内容
print(originData) # [[1, 2], {'name': 'Tom', 'chars': ['A', 'B']}]
print(copyData1) # [[1, 2], {'name': 'Tom', 'chars': ['A', 'B']}]
print(copyData2) # [[1, 2], {'name': 'Tom', 'chars': ['A', 'B']}]
print(copyData3) # [[1, 2], {'name': 'Tom', 'chars': ['A', 'B']}]
print(copyData4) # [[1, 2], {'name': 'Tom', 'chars': ['A', 'B']}]
# 查看所有对象的址,
print(id(originData))# 2308221463360
print(id(copyData1)) # 2308221465664
print(id(copyData2)) # 2308221465792
print(id(copyData3)) # 2308221465728
print(id(copyData4)) # 2308221468544
# 当修改任意对象时,其它对象都会受影响
copyData3[1]["chars"][1] = "BBB"
# 查看所有对象的数据
print(originData)# [[1, 2], {'name': 'Tom', 'chars': ['A', 'BBB']}]
print(copyData1) # [[1, 2], {'name': 'Tom', 'chars': ['A', 'BBB']}]
print(copyData2) # [[1, 2], {'name': 'Tom', 'chars': ['A', 'BBB']}]
print(copyData3) # [[1, 2], {'name': 'Tom', 'chars': ['A', 'BBB']}]
print(copyData4) # [[1, 2], {'name': 'Tom', 'chars': ['A', 'BBB']}]
3、深拷贝
- 深拷贝是指创建一个新的对象,并递归地复制原始对象及其所有嵌套对象的内容,而不仅仅是复制它们的引用。
- 深拷贝具有数据独立性,使用
copy
模块中的deepcopy()
方法实现深拷贝。
import copy
# 原始数据
originData = [[1,2],{"name":"Tom", "chars":["A","B"]}]
# 使用 copy模块中的deepcopy方法获取深拷贝对象
deepCopyData = copy.deepcopy(originData)
# 拷贝成功的验证,内容相同,地址不同
# 查看所有对象内容
print(originData) # [[1, 2], {'name': 'Tom', 'chars': ['A', 'B']}]
print(deepCopyData)# [[1, 2], {'name': 'Tom', 'chars': ['A', 'B']}]
# 查看所有对象的地址,
print(id(originData)) # 2623654162624
print(id(deepCopyData))# 2623654163392
# 当修改任意对象时,其它对象都不会受影响
originData[1]["chars"][1] = "BBB"
# 查看所有对象的数据
print(originData) # [[1, 2], {'name': 'Tom', 'chars': ['A', 'BBB']}]
print(deepCopyData)# [[1, 2], {'name': 'Tom', 'chars': ['A', 'B']}]
4、浅拷贝与深拷贝区别
-
相同点:
- 内容相同,地址不同:都是重新生成一个新内存地址的数据;
-
不同点:
- 1、数据对立性不一样:
- 浅拷贝:不具有数据独立性,当修改任意对象时,其它对象都会受影响;
- 深拷贝:具有数据独立性,当修改任意对象时,其它对象都不会受影响;
- 2、实现方式不一样:
- 浅拷贝:
- 使用对象的copy()方法得到浅拷贝对象;
- 使用工厂方法获取浅拷贝对象;
- 使用切片方式获取浅拷贝对象;
- 使用 copy模块中的copy方法获取浅拷贝对象;
- 深拷贝:
使用copy
模块中的deepcopy()
方法实现深拷贝;
- 浅拷贝:
- 1、数据对立性不一样:
5、说明:
-
程序的大部分场景都使用浅拷贝。
-
浅拷贝,深拷贝特指容器类型保存的复杂结构,对于基本类型的数据,都是引用指向(不在缓存池中的字符串对像除外)。
-
类似公共排序方法
sorted()
实现就可以使用深拷贝,因为该方法返回一个排序后的新列表,该列表可能在程序其它位置被修改,避免影响原列表,深拷贝更适合。