前言
作为一名测试开发工程师,如果简历中写的熟悉python,大概率会被问到浅拷贝和深拷贝的区别。这片内容就详细介绍一下浅拷贝和深拷贝。
拷贝
在Python中,拷贝(copy)是一项常见的操作,用于创建对象的副本。然而,在进行拷贝操作时,我们需要注意深拷贝和浅拷贝的概念以及它们的区别。
深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是两种不同的拷贝方式,它们在拷贝对象时所创建的副本的特性和行为上有所差异。接下来,我们将详细探讨这两种拷贝方式,并通过示例代码来验证结论。
1. 浅拷贝
浅拷贝是一种拷贝方式,它创建一个新的对象,但是只复制原始对象的引用而不是实际数据。简而言之,浅拷贝创建了一个新的对象,但该对象的某些部分仍然与原始对象共享。浅拷贝,是指重新分配一块内存,创建一个新的对象,里面的元素是原对象中子对象的引用。因此,如果原对象中的元素不可变,那倒无所谓;但如果元素可变,浅拷贝通常会带来一些副作用
下面是一个浅拷贝的例子:
import copy
list1 = [1, 2, [3, 4]]
list2 = copy.copy(list1)
# 修改浅拷贝对象中的可变元素
list2[2][0] = 5
print(list1) # 输出:[1, 2, [5, 4]]
print(list2) # 输出:[1, 2, [5, 4]]
在上面的例子中,我们使用copy.copy()进行浅拷贝,创建了一个新的列表对象list2。然后,我们修改了list2中的可变元素[3, 4]的第一个元素为5。结果显示,原始列表list1和浅拷贝后的列表list2都发生了改变,这是因为它们共享了同一个内部列表的引用。
实现浅拷贝的常见方法
可以看到上面的例子,使用copy()函数来实现了浅拷贝。我们在罗列一下其他方法
方法一:使用切片操作符
list1 = [1, 2, [3, 4]]
list2 = list1[:]
方法二:使用list()、dict()、set()等内置函数
list1 = [1, 2, [3, 4]]
list2 = list(list1)
s1 = {1, 2, 3}
s2 = set(s1)
print(s1 == s2) # True
print(s1 is s2) # False
dic1 = {1:[1,2,3]}
dic2 = dict(dic1)
dic1[1].append(4)
2. 深拷贝
深拷贝是一种拷贝方式,它创建一个全新的对象,并且递归地复制原始对象及其所有子对象。简而言之,深拷贝创建了一个与原始对象完全独立的副本。
下面是一个深拷贝的例子:
import copy
list1 = [1, 2, [3, 4]]
list2 = copy.deepcopy(list1)
# 修改深拷贝对象中的可变元素
list2[2][0] = 5
print(list1) # 输出:[1, 2, [3, 4]]
print(list2) # 输出:[1, 2, [5, 4]]
在上面的例子中,我们使用copy.deepcopy()进行深拷贝,创建了一个全新的列表对象list2。然后,我们修改了list2中的可变元素[3, 4]的第一个元素为5。结果显示,原始列表list1保持不变,只有深拷贝后的列表list2发生了改变。
通过以上例子,我们可以看到浅拷贝和深拷贝在拷贝对象时的不同行为。浅拷贝只复制引用,而深拷贝递归地复制整个对象及其子对象。
需要注意的是,在实际开发中,选择使用深拷贝还是浅拷贝取决于具体的需求。如果需要独立的副本,并且避免对原始对象产生任何影响,则应使用深拷贝。而如果只想创建一个新的引用对象,或者拷贝的对象是不可变的,则可以使用浅拷贝。
元组特殊
使用tuple()或切片不会创建浅拷贝,而是会返回一个指向相同元组的引用
t1 = (1, 2, 3)
t2 = tuple(t1)
print(t1 == t2) # True
print(t1 is t2) # False
元组 (1, 2, 3) 只被创建一次,t1 和 t2 同时指向这个元组
即使对于元组进行深拷贝操作,如果元组中的元素是原子类型对象(如整数、字符串等),那么拷贝的过程仍然只会得到一个其内容相同的新元组,而不会产生新的独立对象。
原子类型对象属于不可变对象,深拷贝的过程只会复制其值,并不会创建新的对象。因此,拷贝后的元组与原始元组之间共享相同的原子类型对象。
import copy
t1 = (1, 'a')
t2 = copy.deepcopy(t1)
print(t1 is t2) # 输出:True
最后
总结起来,深拷贝和浅拷贝是Python中两种常见的拷贝方式,它们在拷贝对象时的行为不同。了解它们的区别,可以帮助我们在实际应用中正确选择适当的拷贝方式,以确保我们获得所需的副本。
注意一点:对于非容器类型,如数字、字符,以及其他的“原子”类型,没有拷贝一说,产生的都是原对象的引用。
import copy
a = 1
b = copy.copy(a)
print(a is b) # True