内容简介:假设你去面试 Python 开发岗,面试官如果对基础比较看重的话,那么很可能会问你这样的问题“谈谈你对 Python 中的浅拷贝和深拷贝的理解?”若平时你在开发中像我一样,过度使用 deepcopy,以至于忘记了浅拷贝(shallow copy)和深拷贝(deep copy)的区别,那很可能要栽大跟头了。建议在读这篇文章之前,看下我之前写的文章
假设你去面试 Python 开发岗,面试官如果对基础比较看重的话,那么很可能会问你这样的问题
“谈谈你对 Python 中的浅拷贝和深拷贝的理解?”
若平时你在开发中像我一样,过度使用 deepcopy,以至于忘记了浅拷贝(shallow copy)和深拷贝(deep copy)的区别,那很可能要栽大跟头了。建议在读这篇文章之前,看下我之前写的文章 《你真的理解Python中的赋值、传参吗?》 ,它有助于你更快的理解本文
Python 的引用计数
首先我们要知道,Python 内不可变对象的内存管理方式是引用计数。因此,我们在谈论拷贝时,其实谈论的主要特点都是基于可变对象的。我们来看下面这段代码
import copy a = "张小鸡" b = a c = copy.copy(a) d = copy.deepcopy(a) print "赋值:id(b)->>>", id(b) print "浅拷贝:id(c)->>>", id(c) print "深拷贝:id(d)->>>", id(c) 复制代码
输出如下
赋值:id(b)->>> 4394180400 浅拷贝:id(c)->>> 4394180400 深拷贝:id(d)->>> 4394180400 复制代码
因为我们这里操作的是不可变对象,Python 用引用计数的方式管理它们,所以 Python 不会对值相同的不可变对象,申请单独的内存空间。只会记录它的引用次数
浅拷贝
我们先来比较一下浅拷贝和赋值在可变对象上的区别
import copy a = ["张小鸡"] b = a c = copy.copy(a) print "赋值:id(b)->>>", id(b) print "浅拷贝:id(c)->>>", id(c) 复制代码
输出结果
赋值:id(b)->>> 4473562824 浅拷贝:id(c)->>> 4462057592 复制代码
发现没有,赋值就是对物体进行贴标签操作,作用于同一物体。而浅拷贝则会创建一个新的对象,至于对象中的元素,它依然会引用原来的物体,我们再来看一段例子
import copy a = ["张小鸡"] print "改变前,a内部的元素id:id([a])->>>", [id(_) for _ in a] c = copy.copy(a) print "改变前,浅拷贝c内部的元素id:id([c])->>>", [id(_) for _ in c] a[0] = "姬无命" print "改变后,a内部的元素id:id([a])->>>", [id(_) for _ in a] print "改变后,浅拷贝c内部的元素id:id([c])->>>", [id(_) for _ in c] 复制代码
输出如下
改变前,a内部的元素id:id([a])->>> [4318150256] 改变前,浅拷贝c内部的元素id:id([c])->>> [4318150256] 改变后,a内部的元素id:id([a])->>> [4318150352] 改变后,浅拷贝c内部的元素id:id([c])->>> [4318150256] 复制代码
操作不可变对象时,由于引用计数的特性,被拷贝的元素改变时,就相当于撕掉了原来的标签,重新贴上新的标签一样,对于我们已拷贝的元素没有任何影响。因此在操作不可变对象时,浅拷贝和深拷贝是没有区别的
import copy import json a = [["张小鸡"], "姬无命"] print "改变前,a的值", json.dumps(a, ensure_ascii=False) print "改变前,a内部的元素id:id([a])->>>", [id(_) for _ in a] c = copy.copy(a) print "改变前,c的值", json.dumps(c, ensure_ascii=False) print "改变前,浅拷贝c内部的元素id:id([c])->>>", [id(_) for _ in c] a[0][0] = "Tom" a[1] = "Jack" print "改变后,a的值", json.dumps(a, ensure_ascii=False) print "改变后,c的值", json.dumps(c, ensure_ascii=False) print "改变后,a内部的元素id:id([a])->>>", [id(_) for _ in a] print "改变后,浅拷贝c内部的元素id:id([c])->>>", [id(_) for _ in c] 复制代码
输出结果
改变前,a的值 [["张小鸡"], "姬无命"] 改变前,a内部的元素id:id([a])->>> [4385503208, 4373939232] 改变前,c的值 [["张小鸡"], "姬无命"] 改变前,浅拷贝c内部的元素id:id([c])->>> [4385503208, 4373939232] 改变后,a的值 [["Tom"], "Jack"] 改变后,c的值 [["Tom"], "姬无命"] 改变后,a内部的元素id:id([a])->>> [4385503208, 4373938320] 改变后,浅拷贝c内部的元素id:id([c])->>> [4385503208, 4373939232] 复制代码
由于浅拷贝会使用原始元素的引用(内存地址)。所以在在操作被拷贝对象内部的可变元素时,其结果是会影响到拷贝对象的
深拷贝
深拷贝遇到可变对象,则又会进行一层对象创建,所以你操作被拷贝对象内部的可变对象,不影响拷贝对象内部的值
import copy import json a = [["张小鸡"], "姬无命"] print "改变前,a的值", json.dumps(a, ensure_ascii=False) print "改变前,a内部的元素id:id([a])->>>", [id(_) for _ in a] d = copy.deepcopy(a) print "改变前,d的值", json.dumps(d, ensure_ascii=False) print "改变前,深拷贝d内部的元素id:id([d])->>>", [id(_) for _ in d] a[0][0] = "Tom" a[1] = "Jack" print "改变后,a的值", json.dumps(a, ensure_ascii=False) print "改变后,d的值", json.dumps(d, ensure_ascii=False) print "改变后,a内部的元素id:id([a])->>>", [id(_) for _ in a] print "改变后,深拷贝d内部的元素id:id([d])->>>", [id(_) for _ in d] 复制代码
输出如下
改变前,a的值 [["张小鸡"], "姬无命"] 改变前,a内部的元素id:id([a])->>> [4337440744, 4325876768] 改变前,d的值 [["张小鸡"], "姬无命"] 改变前,深拷贝d内部的元素id:id([d])->>> [4337440888, 4325876768] 改变后,a的值 [["Tom"], "Jack"] 改变后,d的值 [["张小鸡"], "姬无命"] 改变后,a内部的元素id:id([a])->>> [4337440744, 4325875856] 改变后,深拷贝d内部的元素id:id([d])->>> [4337440888, 4325876768] 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Trading and Exchanges
Larry Harris / Oxford University Press, USA / 2002-10-24 / USD 95.00
This book is about trading, the people who trade securities and contracts, the marketplaces where they trade, and the rules that govern it. Readers will learn about investors, brokers, dealers, arbit......一起来看看 《Trading and Exchanges》 这本书的介绍吧!