Python的复制
来源:互联网 发布:linux停机命令 编辑:程序博客网 时间:2024/06/10 03:48
Python的复制
对于Python的复制,相信很多人都有疑惑,因为Python的默认复制是浅复制实现的,因此经常出现各种离奇的问题。要理解好Python的复制机制,需要先理解Python的变量和类型。
不可变对象与可变对象
在Python中,对象可以分为如下两种类型:不可变对象和可变对象。其中不可变对象包括int,float,long,str和tuple等,可变对象则包括list、dict和set等,值得注意的是:这里说的不可变对象中的不可变指的是值不可变。
对于不可变类型的变量,如果要更改变量,则会创建一个新值,把变量绑定到新值上,而旧值如果没有被引用就等待垃圾回收。另外,不可变的类型可以计算hash值,作为字典的key。可变类型数据对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的内存地址会保持不变,但区域会变长或者变短。这个机制可通过如下代码理解:
num = 1print id(num)num += 2print id(num)l = [1, 2, 3]print id(l)l.append(4)print id(l)
该例子中num的两个id是不一样的,表明num变量在不同的取值下指向的内存地址不一样;而l的两个id是一样的,表明l变量在不同的取值下指向的内存地址是一样的。
变量和对象
在Python中,非常重要的一点:“变量无类型,对象有类型”。此点的理解是:Python中的变量是无类型的,但Python是区分类型的。Python的每一个变量其实都是指向内存对象的一个指针,变量都是值得引用,而类型只与对象有关。总结来说:在Python中,类型是属于对象的,而不是变量, 变量和对象是分离的,对象是内存中储存数据的实体,变量则是指向对象的指针。总的来说,Python中类型是属于对象的,变量并没有类型,变量和对象其实是相互分离的,对象是内存中存储数据的实体,而变量只是指向对象的指针。
在不可变对象和可变对象的代码中,可能读者有个疑问,如下代码会改变变量l的id,
l = [1, 2, 3]print id(l)l = [1, 2, 3, 4]print id(l)
的确,上述代码l的两个id不一样,这其中的区别在于开始l指向了[1, 2, 3]的list的对象,当使用append方法时,由于l是可变对象,因此不需要重新再内存中创建新的对象,只需在原来的内存位置增加空间即可。而当使用“=”赋值一个list时,其实在内存是先生成了一个[1, 2, 3, 4]的list对象,然后让l指向该对象,因此出现两个id不一样。
函数的参数传递方式
对于函数的参数传递方式,其实只要理解好变量和对象这一节的内容就很容易理解了,因为Python中变量都是指向对象的指针,而对象才是内存中存储数据的实体,因此函数的参数传递实质上就是让变量指向传入的对象而已。
理解好下面的代码就基本理解了函数的参数传递方式了,下面逐一讲解结果以及原因,
def fun_add_num(num): num += 2def fun_add_list(l): l.append(4)def fun_assign_list(l_1): l_1 = [1, 2, 3, 4]if "__main__" == __name__: num = 1 print num fun_add_num(num) print num l = [1, 2, 3] print l fun_add_list(l) print l l_1 = [1, 2, 3] print l_1 fun_assign_list(l_1) print l_1
(1)在main函数中两次输出的num的值均为1,在fun_add_num中的num开始是指向1这个int对象的,但当执行加法时,由于int对象是不可变对象,因此在fun_add_num中的num变量就变为指向3这个int对象,而main中的num变量依旧指向1这个对象,因此两次输出的num的值均为1
(2)在main函数中两次输出的l的值依次为[1, 2, 3],[1, 2, 3, 4],在fun_add_list中的l开始是指向[1, 2, 3]这个list对象,当使用append方法时,需要改变该对象的值,由于list是可变对象,因此只需要在其后面增加内存空间即可,从而得到这样的结果。
(3)在main函数中两次输出的l_1的值依次为[1, 2, 3],[1, 2, 3],在fun_assign_list中的l_1开始时指向[1, 2, 3]对象,该对象与main函数中l_1指向的对象一致,但当使用赋值时,[1, 2, 3, 4]这个list会在内存中重新申请空间存储,因此此时在在fun_assign_list中的l_1改为指向[1, 2, 3, 4]这个list对象,该对象与main函数中生成的[1, 2, 3]对象没有任何关系,因此出现这样的结果。
浅拷贝和深拷贝
本人这部分主要参考了下面这个博客,读者可以自行前往学习:
http://www.cnblogs.com/wilber2013/p/4645353.html
(1)变量赋值
father = ["father", ["python", "c++"]]son = fatherprint id(father)print fatherprint [id(ele) for ele in father]print id(son)print son print [id(ele) for ele in son]father[0] = "father_0"father[1].append("java")print id(father)print fatherprint [id(ele) for ele in father]print id(son)print son print [id(ele) for ele in son]
运行结果:
解析:
使用变量赋值的方式进行复制时,实质上就是让father变量和son变量指向同一个内存地址(这点可以从两者的id一致验证),因此当改变father指向的list对象的元素时,son同样会改变。
(2)浅拷贝
import copyfather = ["father", ["python", "c++"]]son = copy.copy(father)print id(father)print fatherprint [id(ele) for ele in father]print id(son)print sonprint [id(ele) for ele in son]father[0] = "father_0"father[1].append("java")print id(father)print fatherprint [id(ele) for ele in father]print id(son)print sonprint [id(ele) for ele in son]
运行结果:
解析:
当使用浅复制的方式进行复制时,可以看到father和son两个变量指向的内存地址是不一样的,但两个内存地址的对象的元素指向的地址是一致的。由于”father”的类型是str,为不可变对象,当对象的值需要改变时,重新申请内存空间放置,因此此时father[0]变量指向的地址改变了,而son[0]依旧没有改变,所以father[0]变量指向的内存地址的对象为”father_0”,而son[0]则依旧指向”father”对象。由于father[1]指向的是list对象,该对象为可变对象,因此使用append等方法修改该对象值时只需在原内存空间上增加空间,father[1]指向的地址不需要变,由于son[1]与father[1]指向的地址是一致的,因此会出现father[1]和son[1]同时改变的现象。
(3)深拷贝
import copyfather = ["father", ["python", "c++"]]son = copy.deepcopy(father)print id(father)print fatherprint [id(ele) for ele in father]print id(son)print sonprint [id(ele) for ele in son]father[0] = "father_0"father[1].append("java")print id(father)print fatherprint [id(ele) for ele in father]print id(son)print sonprint [id(ele) for ele in son]
运行结果:
解析:
当使用深复制进行复制时,不仅father和son变量指向的地址不一致,就连father和son的元素指向的地址也不一定一致,不一定一致的原因在于,在python中,相同值得不可变对象的内存地址均一致,因此导致开始时father[0]和son[0]指向了相同的内存地址,但当father[0]指向的对象的值需要改变时,father[0]指向了另外一个地址,此时father[0]和son[0]指向的内存地址就不一致了;而相同值得可变对象的内存则可以不一致,因此开始时father[1]和son[1]指向的内存地址不一致,当father[1]指向的内存对象值改变时,不会影响到son[1]指向的内存对象值。
- 关于python的复制
- python列表的复制
- Python的复制
- python的深复制和浅复制
- python程序的自我复制
- Python 复制文件的方法
- 【python】列表的复制问题
- Python中list的复制
- python中的列表的复制
- Python对象的深复制与浅复制
- Python文件复制中如何复制相关文件的实际操作
- Python中"=“,浅复制和深复制的理解
- python 里的深复制 和浅复制概念
- Python中浅复制和深复制的一点理解
- python复制目录下的文件
- [Python]列表复制的几种方法
- python中复制列表的正确方法
- Python中list和dict的复制
- this solve my problem perfectly
- 深度学习中的Dropout原理介绍
- day7下
- linux学习第十七篇:vim介绍,颜色显示,移动光标,复制剪切粘贴
- 贪心算法——会场安排问题
- Python的复制
- @responseBody注解的使用
- 在linux下安装php扩展时,找不到configure
- 繁花规小程序~
- 第六章 二叉树和树
- hadoop2.7.2本地调试MR IDEA本地调试mapreduce
- 龟兔赛跑
- okhttp通过拦截器输出html源码内容
- Handler源码分析