理解python中的深浅复制
本篇文章主要对深浅复制来一个系统的理解,理解深浅复制还是非常重要的,所以此篇文章主要针对深浅复制还是模糊的朋友一个清晰地认识
什么是深浅复制呢,字面的意思就是复制的深浅程度不一样,浅复制就相当于我们复制了最外面的一层,但是里面的所有东西都是共用的,这么说的话那么我改变其中一个里面的内容,那么另一个不是也变了吗,但是深复制我们里面外面都复制了,那么我们改变其中一个,另一个是一个独立的对象,所以另一个是不会改变的,如果这段文字看不懂后面会有代码分析,再等一下,在对深浅复制理解之前我们先对深浅复制的对象进行分类:
- 可变对象(列表、字典)
- 不可变对象(字符串、元组、数字)
- 可变对象里面还有子对象
不可变对象
我们先对不可变对象进行分析,这个比较简单,在讲解之前再了解一个id(内存)的概念,当内存一样就代表是指向同一个内存地址
- 数字
import copy
s = 1
s1 = copy.copy(s)
s2 = copy.deepcopy(s)
print(id(s))
print(id(s1))
print(id(s2))
# 1347190896
# 1347190896
# 1347190896
- 元组
import copy
s = (1, 2, 3)
s1 = copy.copy(s)
s2 = copy.deepcopy(s)
print(id(s))
print(id(s1))
print(id(s2))
# 2601323093664
# 2601323093664
# 2601323093664
- 字符串
import copy
s = 'hello word'
s1 = copy.copy(s)
s2 = copy.deepcopy(s)
print(id(s))
print(id(s1))
print(id(s2))
# 2366467374640
# 2366467374640
# 2366467374640
观察一下对于不可变对象我们不管深复制还是浅复制最后id都是一样的,也就是内存地址都是一样的,所以我们可以记住不可变对象其实深浅复制是没有什么意义的
可变对象
- 列表
import copy
s = [1, 2, 3]
s1 = copy.copy(s)
s2 = copy.deepcopy(s)
print(id(s))
print(id(s1))
print(id(s2))
# 2193087478472
# 2193086121096
# 2193087479496
s.append(4)
print(s) # [1, 2, 3, 4]
print(s1) # [1, 2, 3]
print(s2) # [1, 2, 3]
在没有嵌套关系的时候,就很好理解,首先对象指向的是不同的内存地址,而列表是可变的,我们现在对原件进行修改,但是只是修改了原件,因为当没有嵌套的时候,那么不管是浅复制还是深复制都复制了原件的所有内容,因为并不存在内层的概念
- 字典
import copy
s = {'1': 'a', '2': 'b'}
s1 = copy.copy(s)
s2 = copy.deepcopy(s)
print(id(s))
print(id(s1))
print(id(s2))
# 1569417887176
# 1569417887248
# 1569418793752
s['1'] = 'c'
print(s) # {'1': 'c', '2': 'b'}
print(s1) # {'1': 'a', '2': 'b'}
print(s2) # {'1': 'a', '2': 'b'}
在没有嵌套的时候字典也很好理解,首先内层地址变了,所以深浅复制成立了,接下来是对字典的内容进行修改,但是还是一样原件变了,复件是没有变的,原理跟列表是一样的
- 字典的嵌套浅复制
import copy
s = {'a': 1, 'b': 2, 'c': [1, 2, 3]}
s1 = copy.copy(s)
print(s) # {'a': 1, 'b': 2, 'c': [1, 2, 3]}
print(s1) # {'a': 1, 'b': 2, 'c': [1, 2, 3]}
s['a'] = 4
s['c'].append(4)
print(s) # {'a': 4, 'b': 2, 'c': [1, 2, 3, 4]}
print(s1) # {'a': 1, 'b': 2, 'c': [1, 2, 3, 4]}
观察一下上面的最后两个打印,我们修改a的值,但是只修改了原件,这个可以对照没有嵌套的字典的浅复制,但是我们增加c的值原件与副本都发生了变化,这是为什么呢,当替换原件中的值时,副本不受影响。然而,如果修改原件中的值(就地修改而不是替换),副本也将发生变化,因为副本指向的也是被修改的值
- 字典的嵌套深复制
import copy
s = {'a': 1, 'b': 2, 'c': ['1', '2', '3']}
s1 = copy.deepcopy(s)
print(s) # {'a': 1, 'b': 2, 'c': [1, 2, 3]}
print(s1) # {'a': 1, 'b': 2, 'c': [1, 2, 3]}
s['a'] = 4
s['c'].append('4')
print(s) # {'a': 4, 'b': 2, 'c': ['1', '2', '3', '4']}
print(s1) # {'a': 1, 'b': 2, 'c': ['1', '2', '3']}
这个就比较好理解,深复制深复制嘛,都重建了一个对象,你原件变不变跟我还有什么关系呢
- 列表的嵌套浅复制
import copy
s = [1, [1, 2], [1, 2, 3]]
s1 = copy.copy(s)
print(s) # [1, [1, 2], [1, 2, 3]]
print(s1) # [1, [1, 2], [1, 2, 3]]
s[1] = [1, 2, 3]
s[2].append(4)
print(s) # [1, [1, 2, 3], [1, 2, 3, 4]]
print(s1) # [1, [1, 2], [1, 2, 3, 4]]
仔细观察一下发现跟字典的浅复制一样,我们修改索引1处的值,但是只修改了原件,复件中索引1处并没有改变,然而,如果修改原件中的值(就地修改而不是替换),副本也将发生变化,因为副本指向的也是被修改的值
- 列表的嵌套深复制
import copy
s = [1, [1, 2], [1, 2, 3]]
s1 = copy.deepcopy(s)
print(s) # [1, [1, 2], [1, 2, 3]]
print(s1) # [1, [1, 2], [1, 2, 3]]
s[1] = [1, 2, 3]
s[2].append(4)
print(s) # [1, [1, 2, 3], [1, 2, 3, 4]]
print(s1) # [1, [1, 2], [1, 2, 3]]
这个也比较好理解,深复制深复制嘛,都重建了一个对象,你原件变不变跟我还有什么关系呢
最后再来一段代码给一个总结,为什么浅复制只是最外面的一层变化了而深复制是里面全部的内容都变化了,其实说到底还是id(内存)的关系
import copy
s = [1, [1, 2], [1, 2, 3]]
s1 = copy.copy(s)
s2 = copy.deepcopy(s)
print(id(s))
print(id(s1))
print(id(s2))
# 2744077336264
# 2744077336200
# 2744077336136
print(id(s[1]))
print(id(s1[1]))
print(id(s2[1]))
# 2744077335240
# 2744077335240
# 2744077336072
print(id(s[2]))
print(id(s1[2]))
print(id(s2[2]))
# 2744075977864
# 2744075977864
# 2744077335944
看仔细了,前三个打印内存地址都不相同,中间三个打印发现前两个已经相同了,也就是说浅复制已经开始共用内存了,但深复制还是不同的内存,后三个打印还是前两个相同,但深复制还是不一样,所以这就是其中的原理
现在大家或许对深浅复制有了一定的了解了,希望多看几遍都能理解这个概念