Python_learn_note_002

Python_learn_note_0021. copy1.1 接口1.2 示例1.2.1 基本类型1.2.2 可变数据类型 如,list1.2.3 含有可变类型的list1.3 总结

1. copy

python的赋值语句不会进行复制对象,只是在目标和对象之间创建绑定。对于可变或者包含可变项的集合(collections)有时候需要一个副本,这样可以改变一个副本而同时保持另一个副本不变。这时候可以使用python的copy模块(copy模块的源码copy.py)。copy模块包含浅拷贝(shallow copy)和深拷贝(deep copy)操作,具体介绍如下。

1.1 接口

copy.copy(x)

返回x的浅拷贝

copy.deepcopy(x)

返回x的深拷贝

expecttion copy.error

抛出特殊模块的异常

浅拷贝和深拷贝的之前的区别仅仅是对复合对象(如list)的拷贝不同

深拷贝操作通常存在两个浅拷贝不存在的问题:

deepcopy()函数通过以下方式避免了这些问题:

此copy模块不进行复制的类型,如模块,方法,堆栈跟踪,堆栈帧,文件,套接字,窗口,数组或任何类似类型。它通过返回原始对象来“复制”函数和类(浅和深);这与pickle模块处理这些方式兼容。

可以使用dict.copy()创建字典的浅层副本,通过分配整个列表的切片来列出列表,例如,copied_list = original_list [:]。

类可以使用相同的接口来控制用于控制pickling的复制。有关这些方法的信息,请参阅模块pickle的说明。实际上,复制模块使用copyreg模块中注册的pickle函数。

为了让类定义自己的副本实现,它可以定义特殊方法__copy __()和__deepcopy __()。前者被称为实现浅拷贝操作;没有传递其他参数。调用后者来实现深拷贝操作;它传递了一个参数,即备忘录字典。如果__deepcopy __()实现需要创建组件的深层副本,它应该调用__deepcopy__()函数,并将组件作为第一个参数,将备注字典作为第二个参数。

1.2 示例

上述来自python官方文档,下面用实例来看看到底区别。

1.2.1 基本类型

此时x, ass_x, shallw_x, deep_x依然是同一个对象。下面对x重新赋值:

此时x的值是2,而ass_x, shallw_x, deep_x 的值是1,x对象和ass_x, shallw_x, deep_x指向了不同的对象,但是ass_x, shallw_x, deep_x依然指向同一个对象。下面继续对ass_x赋值:

此时ass_x和x又指向了同一个对象。继续对shallw_x, deep_x赋值:

从上述示例我们发现对于基本数据,如int,这些不可变的数据类型浅拷贝和深拷贝的作用和赋值操作的作用是相同的,这是因为对于这些类型,copy.copy(x)和copy.deepcopy(x)返回的是x对象本身(具体见源码)。至于为什么当把不同的变量指向同一个数字就成了同一个对象了呢?这个后续会有blog介绍。

1.2.2 可变数据类型 如,list

list是中的存储的数据是可变得,对于这样的数据类型,copy()和deepcopy()的差别如何呢?见下面示例:

我们看到,这时的ass_x和x是指向同一个对象,而x、shallw_x和deep_x分别指向不同的对象。让我们改变x对象中的一个值:

此时发现ass_x的值随着x的值改变而改变,这是因为他们指向的是同一个对象,而shallw_x和deep_x的值没有改变,因为他们指向的是不同的对象。这是浅拷贝和深拷贝对x进行复制得到了另外的不同于x的对象。让我们继续改变shallw_x和deep_x的值:

现在我们发现shallw_x和deep_x改成和x相同的值依然指向的是不同的对象,因为他们是通过对x进行拷贝得到的不同于x的对象。这不同于1.2.1所述的int类型,int类型的数据指向同一个数字时会指向同一个对象,而list却是不同的对象,这和Python本身实现有关,后续会有相关的blog进行介绍,请期待。

到目前为止,我们还未发现浅拷贝和深拷贝有什么任何的差别,别急,继续往下看。

1.2.3 含有可变类型的list

在1.2.2中我们介绍的list中包含的是基本类型,那当list的对象中的元素也是list会是如何的呢?

依然没差别,别急,坚持,马上就看到差别啦,让我们改变x的值看看。

依然没差别,继续,让我们把x中的3改成6试试看

终于看到不同之处啦。没错,这么多的介绍,真正的差别就是这么点儿。但是这点差别却是很大的差别。赋值操作得到的ass_x和x指向的是同一个对象,所以x变时ass_x也变会变;而浅拷贝和深拷贝得到的对象都是对x复制得到一个副本,但是对于对象中的元素是可变的对象时,如list中的元素也是个list,浅拷贝是对子list对象本身进行拷贝,但是对子list的元素不进行拷贝,所以子list的元素依然是同一个对象,所以shallw_x中的子list的元素会跟着x进行变化,而shallw_x中的不可变元素(如1,2)不会跟着x进行变化;但是深拷贝是对list递归进行的拷贝,不仅对子list本身进行拷贝,对子list中的元素也进行拷贝,所以deep_x完全不会跟着x进行变化。

1.3 总结

浅拷贝和深拷贝的差别主要体现在可变对象的元素依然是个可变对象时,如list中的元素也是list。浅拷贝只对list中的元素进行拷贝,而对list中的list元素的元素不进行拷贝,而深拷贝会递归的进行拷贝。