python的深拷贝与浅拷贝
引言
前两天在用python写A*算法的时候,被python的深拷贝和浅拷贝恶搞了一番,实际上还是因为没搞清楚哪些是深拷贝,哪些是浅拷贝,现特意写一篇小结,加深理解。
什么是浅拷贝
所谓浅拷贝,就是复制了地址,实质上是产生了一个指针,指向与之前内容相同的内存块
eg:
可以清楚的看到,b和a指向的是同一块内容,改变b的值,a的值也将随着改变
什么是深拷贝
所谓深拷贝,就是产生了新的地址、申请了新的内存来储存拷贝物
eg:
可以清楚的看到,b复制了a的内容,但是并没有复制a的地址,改变b的值并不会影响a的值
为什么要区分浅拷贝和深拷贝
这就是一个蛋疼的问题了。以我写此文的契机来举例,Astar算法:
Astar算法是一种启发式搜索算法,以八数码问题为例
数字0表示的是空位,其他数字位要一步一步移动成目标态。
算法是这样的:通过移动空位,让初态变成目标态。也就是,通过0和上下左右分别交换,进入下一个状态。具体Astar算法怎样实现,大家有兴趣的话可以搜搜别人的文章,被写烂了。
这里就会出现一个深拷贝和浅拷贝的问题,必须深拷贝一个当前状态的3*3矩阵,再进行移动;
可以这样理解:一个状态要向四个方向移动,得到四个子状态,如果采用浅拷贝,那在向上移动的时候,父矩阵也会跟着改变,变成向上移动;你想要再向下、左、右移动的时候就得不到想要的解了。
python中哪些数据结构是默认深拷贝的
一般而言,非容器类型,int、float、str等,都是浅拷贝,因为开销小所以py默认这些数据结构为深拷贝
python中哪些数据结构是默认浅拷贝的
相反的,list、tuple、dirc、set等容器类型的数据结构,因为深拷贝开销大,所以默认都是浅拷贝的。
列表
----------------------
元祖(元祖的元素是不允许被修改的)
-----------------------
字典
-----------------------
集合(集合元素不允许重复)
浅拷贝元素怎么进行深拷贝呢?
介绍一个内置函数,叫.copy()
你看你看~惊不惊喜,意不意外
需要注意的是,元祖没有这个内置函数,列表、集合、字典都有
通过.copy()这个函数,就可以对数据进行深拷贝
为你哭,为你笑,为你抓狂为你闹
你以为就这点深拷贝浅拷贝知识我会被绕弯?不!你错了,下面来看我真正被绕死的例子
提问,b=a.copy()完成了对a的深拷贝吗?
张三:真的有点猛,这不是明摆着吗,id都不一样了
是的,对于这个长度为3的列表,a[0]、a[1]、a[2]都完成了深拷贝,但是!重点来了,这个列表的第0个元素是不是一个三维的列表啊?每一维都是一个新的列表,那么,对于这个三维列表里面的每一维,进行的是深拷贝还是浅拷贝?
???惊不惊喜,意不意外,我都用了copy了他*的进行的还是浅拷贝!
可以这样理解,copy是用了一个for,扫了一遍每个元素,然后申请了一块新的内存给每个元素
但是,a[0]原本装的就是个指针,指向[[1],[2],[3],[4]],就算申请了新的内存,也不是为实体申请的,而是为指针申请的,用一个新的内存,装了一个旧指针,指向的当然还是旧内容。
对于这种嵌套的容器,他嵌套你也嵌套啊!
用这样一个for循环,扫上面白图的a0,还怕你不深拷贝?
结束语
好,俺终于写完了,俺不是鸽子了