Cover image

Python引用与复制

Nov 19, 2015

Java中除了8中primitive主数据类型外,其他类型基本都是引用(Reference),所以对其的复制我们需要时刻小心究竟是 要对该对象进行一个链接的指向还是要将其复制(Copy)一份。而Python的使用感觉跟java类似,我们也需要关注着复制过 来的究竟是个Reference还是整个的Copy。

在python中,赋值操作总是存储对象的引用,而不是这些对象的拷贝,比如下边一个例子:

>>> a = [1,2,3]
>>> b = [4,5,6]
>>> c = [a,b]
>>> c
[[1,2,3],[4,5,6]]

c列表中是直接对a,b列表的一个引用,所以一旦a,b指向的对象发生变化,c列表也会改变。

>>> a[0] = '123'
>>> c
[['123',2,3],[4,5,6]]

这样可以在程序传递较大的对象而不必将其整个内容进行拷贝。不过有时候我们可能确实需要对对象进行拷贝有该怎么办? 参见《Python学习手册》提供四种明确拷贝的方式:

但需要注意的是,前三项的复制方式都只能算是浅复制,只有copy标准库中的copy.deepcopy()才能提供深复制。那什么是 浅复制和深复制呢?

浅复制:当我们呢的list中含有其他的嵌套对象时,浅复制会将list中的所有项简单的复制到另一个list,也就是说,list中最顶层的元素是什么,它复制的就是什么, 例如第一个例子中,c列表中包含的是a,b列表的引用,所以浅复制的话只是简单的将a,b,这两个引用复制到另一个list中,而如果a,b列表中的 值改变的话,新的list值也会改变,所以并没有摆脱与旧list的关系。

>>> d = c[:]
>>> e = list(c)
>>> import copy
>>> f = copy.copy(c)
>>> a[0] = 'test'
>>> d
[['test',2,3],[4,5,6]]
>>> e
[['test',2,3],[4,5,6]]
>>> f
[['test',2,3],[4,5,6]]

我们可以看到无条件值的分片以及list内置函数以及copy.copy()方法进行的都是浅复制,在a列表改变之后新的list值也会改变了。

深复制:有时候我们需要的是对整个list以及list中的list甚至list中的list中的list的等等全部嵌套的完整的完全独立的一个 复制,需要跟旧有的list完全脱离关系,这时候就只能用到深拷贝,也就是copy模块中的copy.deepcopy()来执行。

>>> g = copy.deepcopy(c)
>>> a[0] = 'testdeepcopy'
>>> g
[['test',2,3],[4,5,6]]

19 Nov 2015

Post by: MetaCoder