深拷贝与浅拷贝
我们常说的深浅拷贝,其实就是传值与引用的区别:
- 深拷贝是指使用一块新的与原对象相同大小的内存,将需要拷贝的对象的所有值都拷贝到新的内存位置中,拷贝出来的对象与原对象互相独立。使用深拷贝赋值,传的是值。
- 浅拷贝是指使用原对象的引用作为拷贝的对象,而不使用新的内存存放拷贝出来的对象,拷贝的对象与原对象共用同一块内存,所以对其中任一个做修改,另一个也会随之改变。使用浅拷贝赋值,传的是引用。
Python中对象的赋值
Python中很多地方都用到浅拷贝,这与C/C++默认的传参方式(除非声明是引用,否则默认传值)是截然不同的。
在Python中,对象的赋值是浅拷贝,当把一个对象赋值给另一个变量的时候,Python只是拷贝了这个对象的引用。
使用Python标准库一些函数,如果不注意它浅拷贝的特性,很容易掉进坑里,出现莫名其妙的bug。
append()函数
给Pyhton的列表尾部追加元素,我们通常会用到append()函数,但是要注意append()函数使用的是浅拷贝。
举个例子:
mylist = []
x = [1, 2, 3]
mylist.append(x)
给空列表mylist追加一个x,此时查看mylist,输出为[[1, 2, 3]]
,即mylist拥有了一个元素,该元素正是x(列表[1, 2, 3]
)。
然后修改x的其中一个元素:
x[1] = 0
修改后x变成[1, 0, 3]
,再次查看mylist,发现它也变成了[[1, 0, 3]]
。这表示在执行语句mylist.append(x)
的时候使用的是浅拷贝,只给mylist追加了x的引用,因此对x作修改就会反映到mylist上来。
本例完整运行截图:
如何使用深拷贝
如果想避免浅拷贝带来的麻烦,直接使用深拷贝,可以使用copy
模块中的deepcopy()
函数:
from copy import deepcopy
mylist = []
x = [1, 2, 3]
mylist.append(deepcopy(x))
还是用刚才的例子,对x的元素作修改,可以看到mylist没有被改动:
此外,如果做数值处理等常会使用numpy
中的ndarray
多维数组对象,如果给列表追加的是这种对象,可以使用numpy.copy()
函数进行多维数组的深拷贝,例如:
import numpy as np
mylist = []
x = np.array([1, 2, 3])
mylist.append(np.copy(x))
然后可以修改x:
x[1] = 0
最后查看mylist,发现并无变化: