写的很详细的博客: Python 浅拷贝copy()、深拷贝deepcopy() 与 赋值(=)的区别,一看就懂
1、赋值 =
赋值是将原对象的引用传递给变量, 并不会产生一个独立的对象单独存在, 它只是将原有的数据贴上一个新标签, 所以原数据改变时, 赋值的变量也随之改变。
2、浅拷贝 copy()
只拷贝父对象(拷贝顶层),不会拷贝对象的内部的子对象。
浅拷贝对于一个复杂对象的子对象并不会完全复制,什么是复杂对象的子对象呢?就比如序列里的嵌套序列,字典里的嵌套序列等都是复杂对象的子对象。
对于子对象,python会把它当作一个公共镜像存储起来,所有对他的复制都被当成一个引用,所以说当其中一个引用将镜像改变了之后另一个引用使用镜像的时候镜像已经被改变了。
所以当改变的值是复杂子对象中的元素时,浅拷贝的值也会随之改变。
3、深拷贝 deepcopy()
拷贝所有对象,包括子对象。
深拷贝会将复杂对象的每一层复制一个单独的个体出来,实现寻常意义上的复制(备份)。
所以要想复制的对象不受原数据改变的影响,只能用深拷贝。
4、代码示例
4.1 简单对象
import copy
a = [1, 2, 3, 4]
b = a # 赋值
c = copy.copy(a) # 浅拷贝
d = copy.deepcopy(a) # 深拷贝
a[3] = 666
print(f"a={a}") # a=[1, 2, 3, 666]
print(f"b={b}") # b=[1, 2, 3, 666]
print(f"c={c}") # c=[1, 2, 3, 4]
print(f"d={d}") # d=[1, 2, 3, 4]
4.2 复杂对象
import copy
a = [1, 2, [3, 3, 3], 4]
b = a # 赋值
c = copy.copy(a) # 浅拷贝
d = copy.deepcopy(a) # 深拷贝
a[2][0] = 666
print(f"a={a}") # a=[1, 2, [666, 3, 3], 4]
print(f"b={b}") # b=[1, 2, [666, 3, 3], 4]
print(f"c={c}") # c=[1, 2, [666, 3, 3], 4]
print(f"d={d}") # d=[1, 2, [3, 3, 3], 4]
5、使用数据拷贝的实例
在 LeetCode 46 全排列 中用到了回溯算法
import copy
class Solution:
def __init__(self):
self.ans=[]
def permute(self, nums: List[int]) -> List[List[int]]:
def backtrack(nodes: List[int]):
if len(nodes)==len(nums):
# 这里必须给ans传入nodes的拷贝,否则nodes在不断改变,ans也会随之改变
# 若直接使用 ans.append(nodes),最终 ans=[[],[]...]
temp=copy.deepcopy(nodes)
self.ans.append(temp)
return
else:
for node in nums:
if node not in nodes:
nodes.append(node)
backtrack(nodes)
nodes.pop()
backtrack([])
return self.ans
其中一个典型错误是直接使用:
if len(nodes)==len(nums):
self.ans.append(nodes)
return
这样得到的答案是:
ans=[[],[]...]
我们来分析一下原因:ans.append(nodes) 其实是将 nodes对象 传入了 ans列表中;如果 nodes 对象发生了改变,则 ans 也会随之改变。因为 nodes 经过多次入栈和出栈后变为空列表 [],所以最终的 ans 也变为了 [ [],[]…]
因此必须使用拷贝防止 ans 被修改
代码示例:
ans=[]
a=[1,2,3]
ans.append(a)
a.pop()
print(a) # [1,2]
print(ans) # [[1,2]],ans 随着 a 的改变而改变