深入浅出Python深拷贝浅拷贝

所谓拷贝,就是复制。
深拷贝,就是复制的比较多,浅拷贝,就是复制的比较少。
在Python中,copy.copy可以完成浅拷贝,copy.deepcopy可以完成深拷贝。

举例 图解

1.赋值

如果 a = [11,22], 把a赋给b:

In [1]: a = [11, 22]
In [2]: b = a

In [3]: a
Out[3]: [11, 22]

In [4]: b
Out[4]: [11, 22]

从结果来看是看不出来a和b是否是同一个,我们用id()函数查看一下a和b的id:

In [5]: id(a)
Out[5]: 2293026594376

In [6]: id(b)
Out[6]: 2293026594376

a 和 b的id相同,说明他们指向了同一块区域,这里说指向,是因为在Python中,实际上是有一个空间存储了[11, 22],在给a赋值的时候,变量名a就指向了[11,22]的空间,当把a赋给b的时候,实际上是让b也指向[11,22]。
画图来说明是这样的:


image.png
2.浅拷贝

在python中,copy.copy()可以用来浅拷贝,copy.deepcopy()可以用来深拷贝。

重新定义了a,b,然后把a和b作为一个列表赋给c。

In [11]: a = [11,22]

In [12]: b = [33,44]

In [13]: c = [a, b]

这时候相当于:


image.png

验证一下:
如果a与c[0]的id相同,就说明他们指向的是相同的区域。

In [14]: id(a)
Out[14]: 2293027197896

In [15]: id(c[0])
Out[15]: 2293027197896

如果现在把c赋给d:

In [16]: d = c

这相当于:


image.png

验证一下:

In [17]: id(d)
Out[17]: 2293026531656

In [18]: id(c)
Out[18]: 2293026531656

如果这时候把c浅拷贝给e:

In [19]: import copy

In [20]: e = copy.copy(c)

这时候,e会指向一块新开辟的区域。


image.png

因为是浅拷贝,所以e的这块区域也会指向c的区域的指向,也就是这样:


image.png

验证:

In [21]: id(e)
Out[21]: 2293027309192

In [22]: id(c)
Out[22]: 2293026531656

In [23]: id(e[0])
Out[23]: 2293027197896

In [24]: id(c[0])
Out[24]: 2293027197896

e和c的地址不同,说明e和c指向的区域不一样,e[0]和c[0]的id相同,说明他们指向了同一块空间。
这就是浅拷贝,它只拷贝了第一层,第二层以下的数据并没有真的“拷贝”出来。
当前的a,b和c,e:

In [25]: a
Out[25]: [11, 22]

In [26]: b
Out[26]: [33, 44]

In [27]: c
Out[27]: [[11, 22], [33, 44]]

In [28]: e
Out[28]: [[11, 22], [33, 44]]

因为是浅拷贝,所以如果a修改了,e也会修改。

In [30]: a
Out[30]: [11, 22, 'hahaha']

In [31]: c
Out[31]: [[11, 22, 'hahaha'], [33, 44]]

In [32]: e
Out[32]: [[11, 22, 'hahaha'], [33, 44]]

a = [11,22] c = [a,b],再来看看深拷贝。

3.深拷贝

如果是深拷贝,就原原本本的把所有层的数据都拷贝一份新的,指向也指向这份新的数据。

In [44]: f = copy.deepcopy(c)

In [45]: c
Out[45]: [[11, 22], [33, 44]]

In [46]: f
Out[46]: [[11, 22], [33, 44]]

这时候的指向是这样的:


image.png

验证:
如果c和f,c[0]和f[0]的地址不同,说明是这样的。

In [47]: id(c)
Out[47]: 2293026784840

In [48]: id(f)
Out[48]: 2293027611400

In [49]: id(c[0])
Out[49]: 2293027354312

In [50]: id(f[0])
Out[50]: 2293027035208

4.总结

所以不管是copy还是deepcopy,都会先创建一份新的空间,而直接赋值b=a则不会。

几个问题

1.问题1

前提如下:

In [53]: a = [11,22]

In [54]: b = [33,44]

In [55]: c = [a, b]

In [56]: d = copy.copy(c)

In [57]: e = copy.deepcopy(c)

如果这时候在c中append一个新的列表[55,66]d和e会如何变化?
查看结果:

In [58]: c.append([55,66])

In [59]: d
Out[59]: [[11, 22], [33, 44]]

In [60]: e
Out[60]: [[11, 22], [33, 44]]

原因图解如下:
一开始的指向是这样的:


image.png

或者把d放在上面更直观一些:


image.png

这时候在c中添加新的列表:
image.png

实际上的这个添加和d和e是没有任何关系的,所以才会有这样的结果,因为d已经拷贝完了,拷贝完之后才添加的,所以d中不会有[55,66]。
问题2

前提:

In [70]: a = (11,22)

In [71]:  b = copy.copy(a)

把a浅拷贝给b,这时候a是一个元组。
来查看a和b的id:

In [73]: id(a)
Out[73]: 2293027979080

In [74]: id(b)
Out[74]: 2293027979080

发现他们的地址是一样的。
所以如果浅拷贝发现拷贝对象是一个元组,就直接不拷贝了,变为了指向。
因为元组是不可变类型,数据不会被修改。
如果是深拷贝:

In [75]: c = copy.deepcopy(a)

这时候查看c的id和a的id:

In [76]: id(c)
Out[76]: 2293027979080

In [77]: id(a)
Out[77]: 2293027979080

他们的地址相同,说明深拷贝也没有拷贝。
再看一种情况:


In [79]: a = [11,22]

In [80]: b = [33,44]

In [81]: c = (a,b)

In [82]: d = copy.copy(c)

In [83]: e = copy.deepcopy(c)

此时a和b都是可变的列表,c是不可变的元组,d有了c的浅拷贝,e有了d的深拷贝。
这时候d和e有没有拷贝c?
来查看一下id:

In [84]: id(c)
Out[84]: 2293028072520

In [85]: id(d)
Out[85]: 2293028072520

In [86]: id(e)
Out[86]: 2293027609416

说明浅拷贝还是没有拷贝,还是指向,但深拷贝拷贝了。
所以:

  • 如果拷贝的是一个元组(第一层是元组),而且元组中都是不可变对象,那么copy模块不管是深拷贝还是浅拷贝都变为了指向(引用)。
  • 如果拷贝的是一个元组(第一层是元组),不管元组中的对象可变还是不可变,浅拷贝都直接指向,而不开辟新的空间了。
  • 如果拷贝的是一个元组(第一层是元组),元组中有可变对象,那么深拷贝还是会开辟新的空间,把每一层的数据都拷贝了。
  • 所以当深拷贝的时候,如果每一层的元素都是不可变对象,那深拷贝也不拷贝了,变为了指向,但是不管哪一层的数据,只要有一个是可变对象,那么深拷贝就会递归的把所有的数据都拷贝一份。
问题3

切片是浅拷贝。

In [88]: a = [11,22]

In [89]: b = [33,44]

In [90]: c = [a,b]

In [91]: d = c[:]

In [92]: id(c)
Out[92]: 2293027355592

In [93]: id(d)
Out[93]: 2293026561416

In [94]: a.append("lalala")

In [95]: a
Out[95]: [11, 22, 'lalala']

In [96]: c
Out[96]: [[11, 22, 'lalala'], [33, 44]]

In [97]: d
Out[97]: [[11, 22, 'lalala'], [33, 44]]
问题4

字典中的拷贝。
字典有一个copy方法,用法如下:

dict.copy()

这个拷贝是一个浅拷贝。

In [100]: first = {"a":[1,2],"b":33}

In [101]: second = first.copy()

查看一下两个字典:

In [102]: first
Out[102]: {'a': [1, 2], 'b': 33}

In [103]: second
Out[103]: {'a': [1, 2], 'b': 33}

如果这时候[11,22]列表中添加一个新的值3,再查看一下:

In [104]: first['a'].append(3)

In [105]: first
Out[105]: {'a': [1, 2, 3], 'b': 33}

In [106]: second
Out[106]: {'a': [1, 2, 3], 'b': 33}

说明是浅拷贝。
补充说明:
字典的key是保存在字典里的,value才是指向。


image.png

补充说明:

在python中,像11这样的值的类型的数据在只会创建一个,所以如果a=[11,22],b=[11,22],实际指向是这样的:

image.png

所以图解的部分画的图并不准确,可以重新看一下~
参考:
https://www.cnblogs.com/shiyublog/p/10809953.html#_label3

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270