Python学习笔记(十一)——赋值、深拷贝与浅拷贝
Python中,对象赋值实际上是对象的引用。即当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了对这个对象的引用地址。简单来讲就是说,在给变量赋值的时候,我们会给变量值开辟一个存放空间,当我们调用这个变量的时候实际上是在调用这个空间中的值:比如 a = "test" ,那么test会有一个存放的空间位置ID,当b = a 的时候实际上是把b的指针指向了test的存储空间ID,而不是重新给b开辟一个新空间存放数据。
常见的有三种方法:
-
直接赋值:其实就是对象的引用(别名)。
-
浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。
-
深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。
一、直接赋值;默认浅拷贝传递对象的引用而已,原始列表改变,被赋值的其它对象也会做相同的改变
a = [1,2,['test1','test2']] b = a print(id(a)) #查看存储空间的id print(id(b)) #查看存储空间的id print('第一次的值:',a) print('第一次的值:',b) a.append('4') print('第二次的值:',a) print('第二次的值:',b) 运行结果: 18635464 18635464 第一次的值: [1, 2, ['test1', 'test2']] 第一次的值: [1, 2, ['test1', 'test2']] 第二次的值: [1, 2, ['test1', 'test2'], '4'] 第二次的值: [1, 2, ['test1', 'test2'], '4']
二、浅拷贝:copy浅拷贝,没有拷贝子对象,所以原始数据改变,子对象会改变
a = [1,2,['test1','test2']] # b = a.copy() b = copy.copy(a) print('a的ID值:',id(a)) print('b的ID值:',id(b)) print('第一次的值a:',a) print('第一次的值b:',b) print('a所有元素的下标值:',[id(ele) for ele in a]) print('b所有元素的下标值:',[id(ele) for ele in b]) a[0]=3 a[2].append('test3') print('第二次的值a:',a) print('第二次的值b:',b) print('a所有元素的下标值:',[id(ele) for ele in a]) print('b所有元素的下标值:',[id(ele) for ele in b]) 运行结果: a的ID值: 18639688 b的ID值: 10909960 第一次的值a: [1, 2, ['test1', 'test2']] 第一次的值b: [1, 2, ['test1', 'test2']] a所有元素的下标值: [1750270448, 1750270480, 18743880] b所有元素的下标值: [1750270448, 1750270480, 18743880] 第二次的值a: [3, 2, ['test1', 'test2', 'test3']] 第二次的值b: [1, 2, ['test1', 'test2', 'test3']] a所有元素的下标值: [1750270512, 1750270480, 18743880] b所有元素的下标值: [1750270448, 1750270480, 18743880] 分析: 首先,创建一个变量a,并指向了一个list类型的对象;
然后通过copy模块里面的浅拷贝函数copy(),对a指向的对象进行浅拷贝,然后浅拷贝生成的心对象赋值给b变量(1、浅拷贝会创建一个新的对象,所以id看的值是不一样的;2、对象中的元素,浅拷贝就只会使用原始元素的引用(内存地址),也就是说"a[i] is b[i]);
最后对a进行修改的时候,由于list的第一个元素是不可变类型,所以will对应的list的第一个元素会使用一个新的对象1750270512;由于第三个元素是可变类型,修改操作不会产生新的对象,所以a的修改结果会相应的反应到b上
三、深度拷贝,包含对象里面的对象的拷贝,所以原始对象的改变不会造成深拷贝里任何子元素的改变
import copy a = [1,2,['test1','test2']] b = copy.deepcopy(a) print('a的ID值:',id(a)) print('b的ID值:',id(b)) print('第一次的值a:',a) print('第一次的值b:',b) print('a所有元素的下标值:',[id(ele) for ele in a]) print('b所有元素的下标值:',[id(ele) for ele in b]) a[2].append('test3') print('第二次的值a:',a) print('第二次的值b:',b) print('a所有元素的下标值:',[id(ele) for ele in a]) print('b所有元素的下标值:',[id(ele) for ele in b]) 运行结果: a的ID值: 18574152 b的ID值: 18674696 第一次的值a: [1, 2, ['test1', 'test2']] 第一次的值b: [1, 2, ['test1', 'test2']] a所有元素的下标值: [1750270448, 1750270480, 18674632] b所有元素的下标值: [1750270448, 1750270480, 18574536] 第二次的值a: [1, 2, ['test1', 'test2', 'test3']] 第二次的值b: [1, 2, ['test1', 'test2']] a所有元素的下标值: [1750270448, 1750270480, 18674632] b所有元素的下标值: [1750270448, 1750270480, 18574536] 分析:
首先,创建一个变量a,并指向了一个list类型的对象;
然后通过copy模块里面的深拷贝函数deepcopy(),对a指向的对象进行深拷贝,然后深拷贝生成的对象赋值给b变量(1、深拷贝会创建一个新的对象,所以id看的值是不一样的;2、对象中的元素,深拷贝都会重新生成一份,而不是简单的使用原始元素的引用(内存地址),也就是说"a[2] is not b[2]);
最后对a进行修改的时候,由于第三个元素是可变类型,修改操作不会产生新的对象,但是由于"a[2] is not b[2]",所以a的修改不会影响b
四、总结
- Python中对象的赋值都是对对象内存地址的引用传递
- 使用copy.copy(a)或者a.copy(),可以进行对象的浅拷贝,它复制了对象,但对于对象中的元素,依然使用原始的引用.
- 如果需要完全拷贝父对象及其子对象(它里面的所有元素(包含元素的子元素)),可以使用copy.deepcopy()进行深拷贝
参考链接:
感谢您的阅读,若有不足之处,欢迎指教,共同学习、共同进步。 博主网址:http://www.cnblogs.com/beginner-boy/ 如您喜欢,麻烦推荐一下;如您有新想法,欢迎提出,邮箱:582238977@qq.com。 本博客为博主原创,欢迎转载,但必须注明博客来源。