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()进行深拷贝

参考链接:

https://www.cnblogs.com/wilber2013/p/4645353.html

猜你喜欢

转载自www.cnblogs.com/beginner-boy/p/12520063.html