首页 > 编程语言 >python-赋值、浅拷贝、深拷贝的区别

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

时间:2022-09-20 10:13:40浏览次数:75  
标签:python list1 数据类型 str 字符串 拷贝 内存地址 id 赋值

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造成影响,反之亦然

 

标签:python,list1,数据类型,str,字符串,拷贝,内存地址,id,赋值
From: https://www.cnblogs.com/trystudy/p/16710064.html

相关文章

  • Python桌面应用开发 ——— PyQT5
    Qt是一个跨平台的C++开发库,主要用来开发图形用户界面(GraphicalUserInterface,GUI)程序,当然也可以开发不带界面的命令行(CommandUserInterface,CUI)程序。PyQT5简介PyQt......
  • Python数据分析教程(一):Numpy
    数据的纬度一维数据:列表和集合类型二维数据:列表类型多维数据:列表类型高维数据:字典类型或数据表示格式,如json、xml、yaml维度:一组数据的组织形式列表和数组:一组......
  • Python第3章实验报告
    一、实验题目Python第三章实例和实战作业二、实验目的和要求1.熟悉Pycharm的运行环境2.学习并掌握Python的流程控制语句三、主要仪器设备联想小新air15硬件:AMDR75......
  • Java自定义类创建对象数组并赋值
    以下代码执行会报错。出错代码:publicclassTest{publicstaticvoidmain(String[]args){Person[]P1=newPerson[2];P1[0].setAge(20);......
  • Python笔记-built-in functions之range class的step参数说明
    class range(start, stop[, step]) Forapositive step,thecontentsofarange r aredeterminedbytheformula r[i] = start + step*i where i >=......
  • 详解Python的装饰器
    来源  https://www.cnblogs.com/tobyqin/p/python-decorator.html Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里。为什么需要装饰......
  • Python 代码智能感知 —— 类型标注与特殊的注释(献给所有的Python人)
    【原文地址:https://xiaokang2022.blog.csdn.net/article/details/126936985】​一个不会写好的类型标注和注释的Python程序员,是让使用TA的代码的人都痛苦无比的事情…......
  • Python查看文件各个时间
    importos,timefilePath='test.txt'#获取文件创建时间戳print(os.path.getctime(filePath))#获取文件的修改时间戳print(os.path.getmtime(filePath))#获取文......
  • python在vscode中的调试--
    如果你使用fromnumpyimport*调试时会把numpy库包含的包都给调试了,产生很多不必要的调试信息如果你只从库中导出你需要的包,会产生更加干净的调试信息比如说你只用了......
  • Python获取以前的日期或以后的日期
    importdatetimefromdateutil.relativedeltaimportrelativedeltat=datetime.datetime.now()#当前日期d=datetime.date.today()print(d)#1天前d1=d-rel......