在上周的Python科学计算课上,老师讲到了Python序列的浅拷贝以及深拷贝方面的知识,个人觉得说得比较言简意赅了,对于我这个刚入Python的新手来说,也基本可以避免今后变量的赋值使用错乱的问题。
这里我们简单的将Python中的标准数据类型分为两类:
- 不可变数据类型:int、float、string、boolean
- 可变(组合)数据类型:列表(list)、字典(dict)、集合(set)
先举几个例子:
1 | a = 1 # a为上述定义的不可变数据类型 |
从上述的例子当中看出,在不可变数据类型中,所定义的变量的值在后来改变(这里是b),并不会引起原来赋给它值的那个量的改变(这里是a);而在组合数据类型中就发生了改变,我们只是将d的值进行了改变,并没有直接改变c的值,最后c的值却也发生了变化。
这里,基本数据变量的赋值其实就是深拷贝;组合数据类型的赋值就是起了一个别名。
这里先做出组合数据类型中赋值、浅拷贝、深拷贝三种的区别:
直接赋值:其实就是对象的引用(即给对象起一个别名)。
浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。
深拷贝(deepcopy):copy模块的deepcopy方法,完全拷贝了父对象及其子对象。
关于内部子对象的概念,下方会再解释。
接下来我们再看一组图(上课ppt图片):
这里的a = {1:[1,2,3]}字典类型。b = a : 赋值引用,a 和 b 都指向同一个对象。可以看出,a,b此刻都指向同一个对象,所以改变b的内容,就是在改变a,b同时所指向的对象的内容,可以理解成b就是a的一个别名。
这里 a = {1:[1,2,3]} , b = a.copy(),这里就是一种浅拷贝的方式。可以看出a 和 b 是一个独立的对象,但他们的子对象还是指向统一对象(是引用)。所以在这里L,M就是对象当中的一个子对象([1,2,3])便是这里的子对象。
举个上述的例子:
1 | import copy |
b = copy.copy(a)
使得b为单独一个对象,但是它和a的子对象指向统一对象。这里的子对象就是[1,2,3](列表子对象)。故当改变b中1键对中的值[1,2,3]时,a也会改变(统一子对象)。但向b中添加值时,便不会对a造成影响,因为这是b自身的对象所拥有的值(和a没有关系)。
那么如何拷贝一个a,但对这个拷贝的对象任意操作时,不会对a产生任何的影响呢?答:采用深拷贝。
如图:
从图中可以清楚的看出:深度拷贝, a 和 b 完全拷贝了父对象及其子对象,两者是完全独立的。
1 | import copy |
总结
对于组合数据类型:
直接赋值:其实就是对象的引用(别名)
浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象
深拷贝(deepcopy):copy模块的deepcopy方法,完全拷贝了父对象及其子对象。