python-赋值、浅拷贝、深拷贝的区别

Python 中有6个标准的数据类型,它们又分为可变对象和不可变对象

不可变对象:Number(数字)、String(字符串)、Tuple(元组)

可变对象:List(列表)、Dictionary(字典)、Set(集合)

可变对象是指,一个对象在不改变其所指向的地址的前提下,可以修改其所指向的地址中的值

不可变对象是指,一个对象所指向的地址上值是不能修改的,如果你修改了这个对象的值,那么它指向的地址就改变了,相当于你把这个对象指向的值复制出来一份,然后做了修改后存到另一个地址上了,但是可变对象就不会做这样的动作,而是直接在对象所指的地址上把值给改变了,而这个对象依然指向这个地址

 

下面每个复制和拷贝,都是以list1为对象操作,list里面有2个元素,[1,2]为列表,为可变对象,'python'为字符串,为不可变对象

赋值

list1 = [[1,2],'python']


#复制:只是复制了list1指向内存地址的引用,list1和list2都指向同一个内存地址,其内元素仍旧指向相同内存地址
list2 = list1
print('复制')
print(f'list2:{list2},\nlist2的内存地址:{str(id(list2))}\n(列表为可变数据类型)的内存地址:{str(id(list2[0]))}\n字符串的内存地址(字符串为不可变数据类型):{str(id(list2[1]))}')
print(f'list1:{list1},\nlist1的内存地址:{str(id(list1))}\n(列表为可变数据类型)的内存地址:{str(id(list1[0]))}\n字符串的内存地址(字符串为不可变数据类型):{str(id(list1[1]))}')
list2[1] =''
list1[0][0] = 2print(f'list2:{list2},\nlist2的内存地址:{str(id(list2))}\n(列表为可变数据类型)的内存地址:{str(id(list2[0]))}\n字符串的内存地址(字符串为不可变数据类型):{str(id(list2[1]))}')
print(f'list1:{list1},\nlist1的内存地址:{str(id(list1))}\n(列表为可变数据类型)的内存地址:{str(id(list1[0]))}\n字符串的内存地址(字符串为不可变数据类型):{str(id(list1[1]))}')

打印输出:

复制
list2:[[1, 2], 'python'],
list2的内存地址:2148111806024
(列表为可变数据类型)的内存地址:2148111826824
字符串的内存地址(字符串为不可变数据类型):2148109419184
list1:[[1, 2], 'python'],
list1的内存地址:2148111806024
(列表为可变数据类型)的内存地址:2148111826824
字符串的内存地址(字符串为不可变数据类型):2148109419184
list2:[[2, 2], '林'],
list2的内存地址:2148111806024
(列表为可变数据类型)的内存地址:2148111806216
字符串的内存地址(字符串为不可变数据类型):2148109340064
list1:[[2, 2], '林'],
list1的内存地址:2148111806024
(列表为可变数据类型)的内存地址:2148111806216
字符串的内存地址(字符串为不可变数据类型):2148109340064

当list1 = list2 进行复制,未改变元素值的时候:

list1和list2指向的内存地址一致,且两者内的可变对象和不可变对象的所指向内存地址也是一致的

当改变list2中元素值和list1中元素值的时候:

list2[1] ='林'
list1[0][0] = 2

list1和list2指向的内存地址仍旧一致,表现为:改变list2的元素值的时候,list1内的元素值也跟着一起改变

list1、list2里面的可变对象和不可变对象的指向的内存地址与之前 未改变元素值的指向的内存地址有所变动。但改变元素值后,list1和list2里面可变对象和不可变对象所指向的内存一致(所以复制,不管对list1还是list2中的可变对象或不可对象的值进行修改,都会影响到另外一个list中所对应的元素值)

 

浅拷贝

list3 = copy.copy(list1)
print('浅拷贝')
print(f'list3:{list3},\nlist3的内存地址:{str(id(list3))}\n(列表为可变数据类型)的内存地址:{str(id(list3[0]))}\n字符串的内存地址(字符串为不可变数据类型):{str(id(list3[1]))}')
print(f'list1:{list1},\nlist1的内存地址:{str(id(list1))}\n(列表为可变数据类型)的内存地址:{str(id(list1[0]))}\n字符串的内存地址(字符串为不可变数据类型):{str(id(list1[1]))}')
list3[1] =''
list3[0][0] = 2
print(f'list3:{list3},\nlist3的内存地址:{str(id(list3))}\n(列表为可变数据类型)的内存地址:{str(id(list3[0]))}\n字符串的内存地址(字符串为不可变数据类型):{str(id(list3[1]))}')
print(f'list1:{list1},\nlist1的内存地址:{str(id(list1))}\n(列表为可变数据类型)的内存地址:{str(id(list1[0]))}\n字符串的内存地址(字符串为不可变数据类型):{str(id(list1[1]))}')
print('='*50)

 

打印输出:

浅拷贝
list3:[[1, 2], 'python'],
list3的内存地址:2934480629576
(列表为可变数据类型)的内存地址:2934480650184
字符串的内存地址(字符串为不可变数据类型):2934478242480
list1:[[1, 2], 'python'],
list1的内存地址:2934480629384
(列表为可变数据类型)的内存地址:2934480650184
字符串的内存地址(字符串为不可变数据类型):2934478242480
list3:[[2, 2], '林'],
list3的内存地址:2934480629576
(列表为可变数据类型)的内存地址:2934480650184
字符串的内存地址(字符串为不可变数据类型):2934478163440
list1:[[2, 2], 'python'],
list1的内存地址:2934480629384
(列表为可变数据类型)的内存地址:2934480650184
字符串的内存地址(字符串为不可变数据类型):2934478242480

 

 

 

 使用copy.copy(list1)对list1进行浅拷贝,生成list3,不对元素值进行改动:

list1与list3所指向的内存地址不一致,但list1、list3中的可变对象与不可变对象的所指向的内存地址仍是一致的

对浅拷贝的list3的元素值进行改动:

list3[1] ='林'
list3[0][0] = 2

list3可变对象列表中的元素值发生改动,但所指向的内存地址无变化,所以list3可变对象的值的改动,会同步到list1上

list3不可变对象字符串的元素值发生改动,但所指向的内存地址发生变化,所以list3不可变对象的值的改动,不会同步到list1上

 同样的道理,对原始数据list1的元素进行改动:

list1[1] ='林'
list1[0][0] = 2

打印输出:

浅拷贝
list3:[[1, 2], 'python'],
list3的内存地址:3089021726536
(列表为可变数据类型)的内存地址:3089021747144
字符串的内存地址(字符串为不可变数据类型):3089020388016
list1:[[1, 2], 'python'],
list1的内存地址:3089021726344
(列表为可变数据类型)的内存地址:3089021747144
字符串的内存地址(字符串为不可变数据类型):3089020388016
list3:[[2, 2], 'python'],
list3的内存地址:3089021726536
(列表为可变数据类型)的内存地址:3089021747144
字符串的内存地址(字符串为不可变数据类型):3089020388016
list1:[[2, 2], '林'],
list1的内存地址:3089021726344
(列表为可变数据类型)的内存地址:3089021747144
字符串的内存地址(字符串为不可变数据类型):3089020308976

 

可以看到,结果与上面对list3中元素值进行修改一致

所以浅拷贝

改变原始数据list1或浅拷贝数据lsit3中可变对象的元素值,都会保持同步,所指向的内存地址一致(在可变类型的数据中,如果存在嵌套的结构类型,浅拷贝只复制最外层的数据,导致内存地址发生变化,里面数据的内存地址不会变)

改变原始数据list1或浅拷贝数据lsit3中不可变对象的元素值,改变后所指向的内存地址发生改变,所以不会对另一部分元素值造成影响

 

 深拷贝

深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。深拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关联

#深拷贝
list4 = copy.deepcopy(list1)
print('深拷贝')
print(f'list4:{list4},\nlist4的内存地址:{str(id(list4))}\n(列表为可变数据类型)的内存地址:{str(id(list4[0]))}\n字符串的内存地址(字符串为不可变数据类型):{str(id(list4[1]))}')
print(f'list1:{list1},\nlist1的内存地址:{str(id(list1))}\n(列表为可变数据类型)的内存地址:{str(id(list1[0]))}\n字符串的内存地址(字符串为不可变数据类型):{str(id(list1[1]))}')
list4[1] =''
list4[0][0] = 2
print(f'list4:{list4},\nlist4的内存地址:{str(id(list4))}\n(列表为可变数据类型)的内存地址:{str(id(list4[0]))}\n字符串的内存地址(字符串为不可变数据类型):{str(id(list4[1]))}')
print(f'list1:{list1},\nlist1的内存地址:{str(id(list1))}\n(列表为可变数据类型)的内存地址:{str(id(list1[0]))}\n字符串的内存地址(字符串为不可变数据类型):{str(id(list1[1]))}')

 

 打印输出:

深拷贝
list4:[[1, 2], 'python'],
list4的内存地址:2071927320264
(列表为可变数据类型)的内存地址:2071927314184
字符串的内存地址(字符串为不可变数据类型):2071924933296
list1:[[1, 2], 'python'],
list1的内存地址:2071927320456
(列表为可变数据类型)的内存地址:2071927320392
字符串的内存地址(字符串为不可变数据类型):2071924933296
list4:[[2, 2], '林'],
list4的内存地址:2071927320264
(列表为可变数据类型)的内存地址:2071927314184
字符串的内存地址(字符串为不可变数据类型):2071924854256
list1:[[1, 2], 'python'],
list1的内存地址:2071927320456
(列表为可变数据类型)的内存地址:2071927320392
字符串的内存地址(字符串为不可变数据类型):2071924933296

 

使用copy.deepcopy(list1)对list1进行深拷贝,生成list4,不对元素值进行改动:

list1与list4所指向的内存地址不一致,但list1、list4中的可变对象列表指向的内存地址发生改动,不可变对象字符串指向的内存地址没有发生改变

我们可以得出结论:

1、 深拷贝对最外层数据是只拷贝数据,会开辟新的内存地址来存放数据

2、深拷贝对里面的不可变数据类型直接复制数据和地址,和可变类型的浅拷贝是相同的效果

对深拷贝的list4中的元素值进行修改:

list4[1] ='林'
list4[0][0] = 2

不管是可变对象,还是不可变对象,都不会对原始数据list1造成影响,反之亦然

 

posted @ 2022-09-20 10:03  测试-13  阅读(93)  评论(0)    收藏  举报