1.浅拷贝
浅拷贝会创建一个新的对象,但是如果原对象中的元素是可变的,那么新对象中的这些元素将是原对象中对应元素的引用(即内存地址相同),而不是它们的拷贝。这意味着,如果你修改了新对象中的这些可变元素,原对象中的相应元素也会被修改。
浅拷贝在需要快速复制对象但不需要完全独立于原对象(特别是当内部元素不可变时)时非常有用。然而,当内部元素是可变类型时,需要特别小心,因为对浅拷贝的修改可能会意外地影响到原对象。
1.1 浅拷贝的实现
在Python中,可以使用copy
模块中的copy()
函数来实现浅拷贝。
import copy
# 定义一个包含可变元素的列表
original_list = [1, 2, [3, 4], 5]
# 使用浅拷贝创建新列表
shallow_copied_list = copy.copy(original_list)
# 检查原始列表和浅拷贝列表的外部元素(非可变元素)是否独立
print(id(original_list) == id(shallow_copied_list)) # False,表明是两个不同的对象
# 检查原始列表和浅拷贝列表中可变元素(列表[3, 4])的引用是否相同
print(id(original_list[2]) == id(shallow_copied_list[2])) # True,表明它们是同一个对象
# 修改浅拷贝列表中的可变元素
shallow_copied_list[2].append(6)
# 检查原始列表是否也被修改了
print(original_list) # 输出 [1, 2, [3, 4, 6], 5],表明原始列表也被修改了
运行结果:
1.2 浅拷贝三种形式
(1)切片操作
如:
lst = [1, 2, [3, 4]]
切片操作:
lst1 = lst[:]
或者
lst1 = [each for each in lst]
说明:[:]
它与[0:]
相似,意思是从0索引拆分到末尾。它返回一个新列表。
(2)工厂函数:
lst1 = list(lst)
(3)copy函数:
lst1 = copy.copy(lst)
浅复制要分两种情况进行讨论:
(1)当浅复制的值是不可变对象(字符串、元组、数值类型)时和“赋值”的情况一样,对象的id值(id()函数用于获取对象的内存地址)与浅复制原来的值相同。
(2)当浅复制的值是可变对象(列表、字典、集合)时会产生一个“不是那么独立的对象”存在。有两种情况:
第一种情况:复制的对象中无复杂子对象,原来值的改变并不会影响浅复制的值,同时浅复制的值改变也并不会影响原来的值。原来值的id值与浅复制原来的值不同。
第二种情况:复制的对象中有复杂子对象(例如列表中的一个子元素是一个列表),如果不改变其中复杂子对象,浅复制的值改变并不会影响原来的值。但是改变原来的值中的复杂子对象的值会影响浅复制的值。
2. 深拷贝
深拷贝在Python中是指创建一个新的对象,并递归地复制原始对象及其所有子对象(包括嵌套的对象)的所有内容,使得新对象与原始对象完全独立,不共享内存地址。因此,对深拷贝后的对象进行修改,不会影响到原始对象。
深拷贝是Python中处理复杂对象拷贝的一种重要方式,它可以帮助我们创建完全独立于原始对象的新对象,从而避免在修改新对象时影响到原始对象。然而,在使用深拷贝时,我们也需要注意其可能带来的性能问题和循环引用问题
1.1 深拷贝的实现
(1) 使用copy
模块的deepcopy()
函数
这是最常用的实现深拷贝的方法。copy
模块是Python标准库的一部分,提供了deepcopy()
函数用于深拷贝操作。
import copy
original_list = [[1, 2], [3, 4]]
deep_copied_list = copy.deepcopy(original_list)
# 修改深拷贝后的列表
deep_copied_list[0].append(5)
# 打印原始列表和深拷贝后的列表,查看是否相互独立
print(original_list) # 输出: [[1, 2], [3, 4]]
print(deep_copied_list) # 输出: [[1, 2, 5], [3, 4]]
(2) 使用pickle
模块的loads()
和dumps()
函数:
pickle
模块可以将Python对象序列化为字节流,也可以将字节流反序列化为Python对象。通过先序列化再反序列化的过程,可以实现深拷贝。
import pickle
original_list = [[1, 2], [3, 4]]
serialized_data = pickle.dumps(original_list)
deep_copied_list = pickle.loads(serialized_data)
# 修改深拷贝后的列表
deep_copied_list[0].append(5)
# 打印原始列表和深拷贝后的列表,查看是否相互独立
print(original_list) # 输出: [[1, 2], [3, 4]]
print(deep_copied_list) # 输出: [[1, 2, 5], [3, 4]]
3. 区别归纳
浅拷贝 | 深拷贝 | |
---|---|---|
定义 | 重新分配内存,创建新对象,但内部元素是原对象中子对象的引用 | 重新分配内存,创建新对象,并递归拷贝原对象中的所有元素到新对象中 |
引用类型处理 | 拷贝引用,不拷贝引用的对象 | 拷贝引用的对象,创建新的对象 |
内存独立性 | 引用类型属性共享内存空间 | 完全独立的内存空间 |
影响 | 对新对象中引用类型属性的修改会影响原对象 | 对新对象的修改不会影响原对象 |
在需要完全独立复制对象,且对原对象无影响时使用深拷贝。
在不需要完全独立复制对象,或对象较小且只包含基本数据类型时,可以使用浅拷贝以提高效率。