Python 深拷贝、浅拷贝(python培训)

网友投稿 247 2022-08-26


Python 深拷贝、浅拷贝(python培训)

深拷贝、浅拷贝

1. 浅拷贝

浅拷贝是对于一个对象的顶层拷贝 通俗的理解是:拷贝了引用,并没有拷贝内容

浅拷贝示意图

使用ipython3编写几个示例来看看:

In [1]: a = [1,2,3,4] In [2]: b = a In [3]: a Out[3]: [1, 2, 3, 4]In [4]: b Out[4]: [1, 2, 3, 4]In [5]: id(a) # 查看变量a的内存地址 Out[5]: 140490275823112In [6]: id(b) # 查看变量b的内存地址 Out[6]: 140490275823112In [7]: import copy In [8]: In [8]: c = copy.copy(a) # 使用copy拷贝 a In [9]: c Out[9]: [1, 2, 3, 4]In [10]: id(c) # 查看变量c的内存地址 Out[10]: 140490271207112In [11]:

从上面的示例来看,​​b = a​​​ 与 ​​c = copy.copy(a)​​​ 这两种方式下,b 与 a 的内存地址都是 ​​140490275823112​​​ ,但是 c 的内存地址却是 ​​140490271207112​​​ 。c 已经指向了另一个内存地址了。 说明: ​​​b = a​​ 符合浅拷贝的规则。

思考:既然浅拷贝都是指向同一个内存地址,那么是不是修改一个变量的话,是不是另一个变量指向的值都会一起修改呢?

# 首先查看一下上一个步骤之后, a b c 三个变量的值In [11]: c Out[11]: [1, 2, 3, 4]In [12]: a Out[12]: [1, 2, 3, 4]In [13]: b Out[13]: [1, 2, 3, 4]# 因为 a b 两个变量都是指向同一个内存地址,那么给 b 的list增加一个 5,查看一下变量的修改In [14]: b.append(5) In [15]: b Out[15]: [1, 2, 3, 4, 5]In [16]: a Out[16]: [1, 2, 3, 4, 5]In [17]: c Out[17]: [1, 2, 3, 4] # 可以从上面三个变量看出,a与b变量是同时修改了,而c因为不同内存地址,所以并没有修改。# 那么修改变量c,增加一个数字6到list中,当然是不会影响变量a与b的,实践看看。In [18]: c.append(6) In [19]: c Out[19]: [1, 2, 3, 4, 6]In [20]: a Out[20]: [1, 2, 3, 4, 5]In [21]: b Out[21]: [1, 2, 3, 4, 5]In [22]:

注意: 其实上面的理解对于浅拷贝是有一定的偏差的,虽然 b = a 的确属于浅拷贝的一种,但是浅拷贝 c = copy.copy(a) 也是属于浅拷贝的另一种,那么为什么内存不一样呢?

其实浅拷贝只是拷贝最上面的那一层数据,其实也是会生成一个新的变量,此时内存就会不一样。下面再来一个示例演示一下:

In [22]: d = [a,b] # 首先设置变量 d 加入 a 与 b In [23]: e = copy.copy(d) # 使用 e 浅拷贝 d,此时就会生成一个新的内存变量 e In [24]: id(d) # 查看d的变量内存 Out[24]: 140490271478024In [25]: id(e) # 查看e的变量内存,果然是跟 d 不同的。但是e 浅拷贝过来的 a 与 b 的地址呢? Out[25]: 140490295815880In [26]: id(d[0]) # 首先查看 变量 a 在 d 的list地址 Out[26]: 140490275823112In [27]: id(e[0]) # 查看变量 a 在 e 的list内存地址,居然是一样的。 Out[27]: 140490275823112In [28]: id(d[1]) Out[28]: 140490275823112In [29]: id(e[1]) Out[29]: 140490275823112In [30]:

从上面的结果来看,c 与 d 的变量内存地址不一样,但是 c 与 d 里面的 a 和 b 的内存地址是一样的。 那么是不是就是如果修改 a ,那么 c 与 d 会同时修改呢?

In [30]: a.append(7) In [31]: a Out[31]: [1, 2, 3, 4, 5, 7]In [32]: b Out[32]: [1, 2, 3, 4, 5, 7]In [33]: e[0] Out[33]: [1, 2, 3, 4, 5, 7]In [34]: d[0] Out[34]: [1, 2, 3, 4, 5, 7]In [35]:

答案是会同时修改的,因为内存地址都一致。

这里提供一个理解示意图:

2. 深拷贝

深拷贝是对于一个对象所有层次的拷贝(递归)

In [35]: a = [11,22] In [36]: b = copy.deepcopy(a) # 对a指向的列表进行深copy In [37]: a Out[37]: [11, 22]In [38]: b Out[38]: [11, 22]In [39]: id(a) # 查看 a 的内存地址 Out[39]: 140490295123912In [40]: id(b) # 查看 b 的内存地址,可以看出与变量 a 是不一致的。 Out[40]: 140490295270088In [43]: a.append(33) # 那么 a 增加一个变量 33 肯定不会影响 b,执行看看 In [44]: a Out[44]: [11, 22, 33]In [45]: b Out[45]: [11, 22]In [46]:

但是从这个例子,看不出深拷贝特殊之处。

进一步理解深拷贝

从前面浅拷贝的例子中,我们来看看使用深拷贝有什么变化。

In [46]: a = [1,2,3,4] In [47]: b = a In [48]: d = [a,b] In [49]: e = copy.deepcopy(d) # 使用 e 深拷贝 d ,此时会深度递归 d 里面的所有变量 In [50]: id(d) # 查看变量 d 的内存 Out[50]: 140490296753416In [51]: id(e) # 查看变量 e 的内存,可以看出 e 与 d 的内存地址是不同的。 Out[51]: 140490295210696# 前面案例中,d[0] 的内存地址 是 与 c[0] 的内存地址是一样的,这里来看深拷贝是不一样In [52]: id(d[0]) Out[52]: 140490271451720In [53]: id(e[0]) Out[53]: 140490294520008In [54]: id(e[1]) Out[54]: 140490294520008In [55]: id(d[1]) Out[55]: 140490271451720# 从上面打印来看,深拷贝后,e 与 d 的所有内存变量都是不同的了。In [56]: d Out[56]: [[1, 2, 3, 4], [1, 2, 3, 4]]In [57]: e Out[57]: [[1, 2, 3, 4], [1, 2, 3, 4]]In [58]: a.append(5) In [59]: d Out[59]: [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]In [60]: e Out[60]: [[1, 2, 3, 4], [1, 2, 3, 4]]In [61]:

3. 拷贝的其他方式

分片表达式​​d = c[:]​​可以赋值一个序列

In [1]: a = [11,22] In [2]: b = [33,44] In [3]: c = [a,b] In [4]: d = c[:] # 分片表达式 In [5]: c Out[5]: [[11, 22], [33, 44]]In [6]: d Out[6]: [[11, 22], [33, 44]]# 查看使用分片表达式传值后的变量内存,可以看出 c 与 d 是不同的。In [7]: id(c) Out[7]: 140089352809416In [8]: id(d) Out[8]: 140089352792904# 那么看看 c[0] 与 d[0] 的内存变量,从结果来看是相同的。In [9]: id(c[0]) Out[9]: 140089352742024In [10]: id(d[0]) Out[10]: 140089352742024# 那么既然内存地址都相同,那么给a增加一个33的变量,查看是否同时修改值In [11]: a Out[11]: [11, 22]In [12]: a.append(33) In [13]: a Out[13]: [11, 22, 33]In [14]: c Out[14]: [[11, 22, 33], [33, 44]]In [15]: d Out[15]: [[11, 22, 33], [33, 44]]In [16]:

从上面的结果来看,分片表达式就是一种浅拷贝。

字典的copy方法可以拷贝一个字典

In [16]: import copy # 创建一个字典In [17]: d = dict(name="zhangsan",age=27) # 将字典d 拷贝到 coIn [18]: co = d.copy() # 查看一下 d 与 co 的值In [19]: d Out[19]: {'name': 'zhangsan', 'age': 27}In [20]: co Out[20]: {'name': 'zhangsan', 'age': 27}# 查看一下 d 与 co的内存值,可以看出是不同的。In [21]: id(d) Out[21]: 140089352402120In [22]: id(c) Out[22]: 140089352809416In [23]: # 那么直接给 d 设置新的字典内容In [23]: d = dict(name="zhangsan",age=27,children_ages = [11,22]) In [24]: d Out[24]: {'name': 'zhangsan', 'age': 27, 'children_ages': [11, 22]}In [26]: co Out[26]: {'name': 'zhangsan', 'age': 27}# 重新将 d 拷贝到 coIn [27]: co = d.copy() In [28]: co Out[28]: {'name': 'zhangsan', 'age': 27, 'children_ages': [11, 22]}# 给字典里面的 children_ages 增加 list 数字 9In [29]: d["children_ages"].append(9) # 可以看到 co 也跟着一起变化了,说明 children_ages 的内存地址是一致的。In [30]: d Out[30]: {'name': 'zhangsan', 'age': 27, 'children_ages': [11, 22, 9]}In [31]: co Out[31]: {'name': 'zhangsan', 'age': 27, 'children_ages': [11, 22, 9]}In [32]: In [32]: id(d["children_ages"]) Out[32]: 140089267051336In [33]: id(co["children_ages"]) Out[33]: 140089267051336In [34]:

4. 注意点

浅拷贝对不可变类型和可变类型的copy不同

copy.copy对于可变类型,会进行浅拷贝copy.copy对于不可变类型,不会拷贝,仅仅是指向

# 拷贝list可变类型In [34]: a = [11,22,33] In [35]: b = copy.copy(a) In [36]: id(a) Out[36]: 140089256561608In [37]: id(b) Out[37]: 140089352086664In [38]: a.append(44) In [39]: a Out[39]: [11, 22, 33, 44]In [40]: b Out[40]: [11, 22, 33]In [41]: # 使用元祖再来演示一下,可以看出拷贝的两个变量内存地址一致。In [41]: a = (11,22,33) In [42]: b = copy.copy(a) In [43]: id(a) Out[43]: 140089283270624In [44]: id(b) Out[44]: 140089283270624In [45]:

copy.copy和copy.deepcopy的区别

copy.copy

In [45]: a = [11,22] # 使用元组来括起来 a ,那么后续的内存地址就不会变In [46]: b = (a, ) In [47]: b Out[47]: ([11, 22],)In [48]: c = [b,] In [49]: c Out[49]: [([11, 22],)]# 使用 d 浅拷贝 c,那么 c 里面的元组内存地址会不会变化呢?In [50]: d = copy.copy(c) In [51]: d Out[51]: [([11, 22],)]# 首先查看一下变量 c 与 d 的内存地址,发现是不同的,正常。In [52]: id(c) Out[52]: 140089267047816In [53]: id(d) Out[53]: 140089352213384# 查看 c 与 d 的元组内存地址,发现是一样的,那么就是最初的变量 a 的地址In [57]: id(c[0]) Out[57]: 140089352719216In [58]: id(d[0]) Out[58]: 140089352719216# 给变量 a 增加一个 33的数字,那么 c 与 d 就会同时一起增加,如下:In [54]: a.append(33) In [55]: c Out[55]: [([11, 22, 33],)]In [56]: d Out[56]: [([11, 22, 33],)]

来看看完成使用可变的list示例

In [60]: a = [11,22] In [61]: b = [a] In [62]: b Out[62]: [[11, 22]]In [63]: c = [b] In [64]: c Out[64]: [[[11, 22]]]In [65]: d = copy.copy(c) In [66]: d Out[66]: [[[11, 22]]]In [67]: id(d) Out[67]: 140089282147016In [68]: id(c) Out[68]: 140089352347848In [69]: id(d[0]) Out[69]: 140089356847432In [70]: id(c[0]) Out[70]: 140089356847432In [71]: id(d[0][0]) Out[71]: 140089282502536In [72]: id(c[0][0]) Out[72]: 140089282502536In [73]: c Out[73]: [[[11, 22]]]In [74]: d Out[74]: [[[11, 22]]]In [75]: a Out[75]: [11, 22]In [76]: a.append(33) In [77]: c Out[77]: [[[11, 22, 33]]]In [78]: d Out[78]: [[[11, 22, 33]]]In [79]: a Out[79]: [11, 22, 33]In [80]:

从上面的操作来看,只要是浅拷贝可变的变量,那么内部的数据都是会指向同一个内存地址的。

copy.deepcopy


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Python import导入模块 - reload() 导入的内容
下一篇:Python 进程之间的通信 - 队列Queue(python下载安装教程)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~