假设你去面试 Python 开发岗,面试官若是对基础比较看重的话,那么极可能会问你这样的问题html
“谈谈你对 Python 中的浅拷贝和深拷贝的理解?”python
若平时你在开发中像我同样,过分使用 deepcopy,以致于忘记了浅拷贝(shallow copy)和深拷贝(deep copy)的区别,那极可能要栽大跟头了。建议在读这篇文章以前,看下我以前写的文章《你真的理解Python中的赋值、传参吗?》,它有助于你更快的理解本文git
首先咱们要知道,Python 内不可变对象的内存管理方式是引用计数。所以,咱们在谈论拷贝时,其实谈论的主要特色都是基于可变对象的。咱们来看下面这段代码github
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 不会对值相同的不可变对象,申请单独的内存空间。只会记录它的引用次数json
咱们先来比较一下浅拷贝和赋值在可变对象上的区别bash
import copy
a = ["张小鸡"]
b = a
c = copy.copy(a)
print "赋值:id(b)->>>", id(b)
print "浅拷贝:id(c)->>>", id(c)
复制代码
输出结果spa
赋值:id(b)->>> 4473562824
浅拷贝:id(c)->>> 4462057592
复制代码
发现没有,赋值就是对物体进行贴标签操做,做用于同一物体。而浅拷贝则会建立一个新的对象,至于对象中的元素,它依然会引用原来的物体,咱们再来看一段例子3d
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]
复制代码
输出以下code
改变前,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]
复制代码
所以,在下次咱们遇到这类问题时,咱们说出如下关键点,基本就很稳了