前言
在Python编程中,理解对象的复制是至关重要的。Python提供了两种主要的复制方法:浅复制(shallow copy)和深复制(deep copy)。这两种方法在处理可变对象时有着显著的区别。下面我们简单看一下两者的使用。
什么是浅复制
浅复制指是指创建一个新对象,这个新对象是原对象的一部分或者全部的副本,但对于原对象中嵌套的对象(比如列表中的列表),浅复制仅仅复制它们的引用,也就是说浅复制不会递归复制所有的子对象。
浅复制示例代码
import copy
lst1 = [1,2,[4,5],[6,7]]
lst2 = copy.copy(lst1)
lst1[1] = 1
print(lst1) # [1,1,[4,5],[6,7]]
print(lst2) # [1,2,[4,5],[6,7]]
lst2[2].append(8)
print(lst1) # [1, 1, [4, 5, 8], [6, 7]]
print(lst2) # [1, 2, [4, 5, 8], [6, 7]]
print(id(lst1[2])) # 2626206798336
print(id(lst2[2])) # 2626206798336
输出结果
更新非嵌套对象
原对象[1, 1, [4, 5], [6, 7]]
复制对象[1, 2, [4, 5], [6, 7]]
更新嵌套对象
原对象[1, 1, [4, 5, 8], [6, 7]]
复制对象[1, 2, [4, 5, 8], [6, 7]]
lst1[2]的id:2838000534784
lst1[2]的id:2838000534784
我们看到,lst1和lst2中的非嵌套对象都互不干扰,说明浅复制中非嵌套对象都指向了不同的地址,而嵌套对象的修改是同步的,而且id是相同的,说明浅复制中,原对象和复制对象共享着嵌套对象。
什么是深复制
深复制则是创建一个新对象,并递归地复制所有子对象。这样,无论原对象中的嵌套对象如何变化,深复制的对象都不会受影响。
深复制代码示例
import copy
lst1 = [1,2,[4,5],[6,7]]
lst2 = copy.deepcopy(lst1)
print('更新非嵌套对象')
lst1[1] = 1
print(f'原对象{lst1}')
print(f'复制对象{lst2}')
print('\n')
print('更新嵌套对象')
lst2[2].append(8)
print(f'原对象{lst1}')
print(f'复制对象{lst2}')
id1 = id(lst1[2])
id2 = id(lst2[2])
print(f'lst1[2]的id:{id1}')
print(f'lst1[2]的id:{id2}')
输出结果
更新非嵌套对象
原对象[1, 1, [4, 5], [6, 7]]
复制对象[1, 2, [4, 5], [6, 7]]
更新嵌套对象
原对象[1, 1, [4, 5], [6, 7]]
复制对象[1, 2, [4, 5, 8], [6, 7]]
lst1[2]的id:1863229589376
lst1[2]的id:1863229564992
我们可以看到,深复制和浅复制相比,不管是嵌套对象还是非嵌套对象都是互不干扰的,复制对象完全重新生成了原对象里面的所有子对象。
深复制和浅复制的应用
什么时候使用浅复制
- 当对象中没有嵌套的可变对象时。
- 当你希望新对象和原对象共享嵌套对象时。
什么时候使用深复制
- 当对象中包含嵌套的可变对象,且希望完全独立的副本时。
- 当你不希望两个对象之间有任何共享的引用时。
复制对象的注意事项
- 性能:深复制因为要递归地复制对象及其子对象,可能会带来较大的性能开销。在处理大型数据结构时,应慎重使用深复制。
- 对象类型:不是所有对象都可以被深复制。例如,文件对象、数据库连接等往往不支持深复制。
- 循环引用:如果对象之间存在循环引用,深复制可能会导致递归错误。Python的copy模块可以处理这类情况,但需要注意潜在的性能和内存问题。
总结
浅复制和深复制是Python中处理对象复制的两种重要方法。了解它们的区别及使用场景,对于编写健壮且高效的代码至关重要。在使用复制时,一定要结合自己的场景,谨慎选择复制对象的方式。