当前位置 : 主页 > 编程语言 > python >

Python 深拷贝、浅拷贝

来源:互联网 收集:自由互联 发布时间:2022-07-05
仅供学习参考,转载请注明出处 深拷贝、浅拷贝 1. 浅拷贝 浅拷贝是对于一个对象的顶层拷贝 通俗的理解是:拷贝了引用,并没有拷贝内容 浅拷贝示意图 使用ipython3编写几个示例来看

仅供学习参考,转载请注明出处

深拷贝、浅拷贝

1. 浅拷贝

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


Python 深拷贝、浅拷贝_浅拷贝

浅拷贝示意图

使用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]: 140490275823112

In [6]: id(b) # 查看变量b的内存地址
Out[6]: 140490275823112

In [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]: 140490271207112

In [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]: 140490271478024

In [25]: id(e) # 查看e的变量内存,果然是跟 d 不同的。但是e 浅拷贝过来的 a 与 b 的地址呢?
Out[25]: 140490295815880

In [26]: id(d[0]) # 首先查看 变量 a 在 d 的list地址
Out[26]: 140490275823112

In [27]: id(e[0]) # 查看变量 a 在 e 的list内存地址,居然是一样的。
Out[27]: 140490275823112

In [28]: id(d[1])
Out[28]: 140490275823112

In [29]: id(e[1])
Out[29]: 140490275823112

In [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]:

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

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


Python 深拷贝、浅拷贝_深拷贝_02


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]: 140490295123912

In [40]: id(b) # 查看 b 的内存地址,可以看出与变量 a 是不一致的。
Out[40]: 140490295270088

In [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]: 140490296753416

In [51]: id(e) # 查看变量 e 的内存,可以看出 e 与 d 的内存地址是不同的。
Out[51]: 140490295210696

# 前面案例中,d[0] 的内存地址 是 与 c[0] 的内存地址是一样的,这里来看深拷贝是不一样
In [52]: id(d[0])
Out[52]: 140490271451720

In [53]: id(e[0])
Out[53]: 140490294520008

In [54]: id(e[1])
Out[54]: 140490294520008

In [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]: 140089352809416

In [8]: id(d)
Out[8]: 140089352792904

# 那么看看 c[0] 与 d[0] 的内存变量,从结果来看是相同的。
In [9]: id(c[0])
Out[9]: 140089352742024

In [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]:

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

Python 深拷贝、浅拷贝_深拷贝_03


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

In [16]: import copy

# 创建一个字典
In [17]: d = dict(name="zhangsan",age=27)

# 将字典d 拷贝到 co
In [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]: 140089352402120

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

In [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 拷贝到 co
In [27]: co = d.copy()

In [28]: co
Out[28]: {'name': 'zhangsan', 'age': 27, 'children_ages': [11, 22]}

# 给字典里面的 children_ages 增加 list 数字 9
In [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]: 140089267051336

In [33]: id(co["children_ages"])
Out[33]: 140089267051336

In [34]:


Python 深拷贝、浅拷贝_深拷贝_04


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]: 140089256561608

In [37]: id(b)
Out[37]: 140089352086664

In [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]: 140089283270624

In [44]: id(b)
Out[44]: 140089283270624

In [45]:


Python 深拷贝、浅拷贝_浅拷贝_05


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]: 140089267047816

In [53]: id(d)
Out[53]: 140089352213384

# 查看 c 与 d 的元组内存地址,发现是一样的,那么就是最初的变量 a 的地址
In [57]: id(c[0])
Out[57]: 140089352719216

In [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],)]


Python 深拷贝、浅拷贝_深拷贝_06


来看看完成使用可变的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]: 140089282147016

In [68]: id(c)
Out[68]: 140089352347848

In [69]: id(d[0])
Out[69]: 140089356847432

In [70]: id(c[0])
Out[70]: 140089356847432

In [71]: id(d[0][0])
Out[71]: 140089282502536

In [72]: id(c[0][0])
Out[72]: 140089282502536

In [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]:

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




Python 深拷贝、浅拷贝_深拷贝_07


copy.deepcopy


Python 深拷贝、浅拷贝_浅拷贝_08




Python 深拷贝、浅拷贝_浅拷贝_09




Python 深拷贝、浅拷贝_浅拷贝_10

Python 深拷贝、浅拷贝_浅拷贝_11


关注微信公众号,回复【资料】、Python、PHP、JAVA、web,则可获得Python、PHP、JAVA、前端等视频资料。


上一篇:Python import导入模块 - reload() 导入的内容
下一篇:没有了
网友评论