深拷贝和浅拷贝也是python的一个基础知识点,今天好好研究了下,其实深拷贝和浅拷贝问题就是内存中指针的指向问题。我是这么理解的,有不同意见,欢迎指出。
我就不拆开讲了,一起讲,三种情况,一种是直接赋值引用 即b,一种是浅拷贝即c,一种是深拷贝即d。
>>> import copy #这个需要导入下的 之前还以为是自带的函数报错了
>>> a = [1,2,[3,4]]
>>> b = a #直接赋值
>>> c = copy.copy(a) #浅拷贝 c
>>> d = copy.deepcopy(a) #深拷贝 d
>>> a.append(5)
>>> a
[1, 2, [3, 4], 5]
>>> b
[1, 2, [3, 4], 5]
>>> c
[1, 2, [3, 4]]
>>> d
[1, 2, [3, 4]]
>>> id(a)
4432492592
>>> id(b)
4432492592
>>> id(c)
4432493024
>>> id(d)
4432493888
#在a后面加了一个值之后可以看到 b随a改变而改变了,而c,d并没有改变,看了他们的id之后,我们发现,b和a指向的是同一个地址,而c,d则是新创建了一个地址。
>>> a[2].append(6)
>>> a
[1, 2, [3, 4, 6], 5]
>>> b
[1, 2, [3, 4, 6], 5]
>>> c
[1, 2, [3, 4, 6]]
>>> d
[1, 2, [3, 4]]
>>> id(a[1])
140220084269312
>>> id(b[1])
140220084269312
>>> id(c[1])
140220084269312
>>> id(d[1])
140220084269312
>>> id(a[2])
4432552592
>>> id(b[2])
4432552592
>>> id(c[2])
4432552592
>>> id(d[2])
4432332992
#然后我们改变a中的字列表,可以看到,浅拷贝的改变了,但是深拷贝没有改变,我们看他们的id发现,在拷贝的时候,浅拷贝时候,列表中的每个元素直接指向原来列表中a的每个元素的地址,(因为只指向了a之前存在的元素地址,所以在append操作的时候并不会同步)包括子列表(可变的元素),但是对于深拷贝,它自己新建了个地址,递归复制了原来的内容,所以当原来的改变的时候,拷贝的值并没有改变。相同的,如果a[0]改变了,c,d也不会改变,因为a指向了一个新地址,而b,c依然指向原来的地址。
所谓“浅拷贝”,是指创建一个新的对象,其内容是原对象中元素的引用。(拷贝组合对象,不拷贝子对象)
常见的浅拷贝有:切片操作、工厂函数、对象的copy()方法、copy模块中的copy函数。
所谓“深拷贝”,是指创建一个新的对象,然后递归的拷贝原对象所包含的子对象。深拷贝出来的对象与原对象没有任何关联。
深拷贝只有一种方式:copy模块中的deepcopy函数。
看了上面的例子,有人可能会疑惑:
为什么使用了深拷贝,a和b中元素的id还是一样呢?
答:这是因为对于不可变对象,当需要一个新的对象时,python可能会返回已经存在的某个类型和值都一致的对象的引用。而且这种机制并不会影响 a 和 b 的相互独立性,因为当两个元素指向同一个不可变对象时,对其中一个赋值不会影响另外一个。
参考文章
http://blog.youkuaiyun.com/lisonglisonglisong/article/details/38573269