深浅拷贝

深浅拷贝的对象

首先需要明确一点:在什么情况下讨论深浅拷贝。对于不可变数据类型的变量不存在深浅拷贝问题。深浅拷贝讨论的对象是可变数据类型的变量,如列表、字典等。

看图说事

变量赋值

list1 = [1, 2, [11, 22]]
list2 = list1		# 变量赋值操作

图1是是描述变量赋值操作,计算机内存中发生的变化。

变量赋值不是拷贝操作,因为变量名list1和变量名list2指向的是一块相同的内存地址。赋值操作后,无论是通过list1还是list2修改列表的元素,都只是修改列表容器里面的元素保存的内存地址,而变量名list1和变量名list2绑定的内存地址是列表容器。瓶子里面的东西换了,因为'瓶子'没换,所以数据还是绑定在一块的。通过变量名list1和变量名list2访问到的还是相同的'瓶子'。


浅拷贝

list1 = [1, 2, [11, 22]]
list2 = list1.copy()		# 调用列表内置方法copy(),发生的是浅拷贝,拷贝一个新列表

# 浅拷贝后:id(list1) 不等于 id(list2),证明是拷贝了一个新列表。

图2是描述列表浅拷贝操作,计算机内存中发生的变化。

对列表对象执行浅拷贝操作,将在开辟一块新的内存空间,然后将list2绑定到这块内存空间中。这块内存空间中有三个位置用来存放其三个元素的内存地址。对于浅拷贝,不区分对待元素的是不是可变还是不可变的数据类型。新拷贝的列表的三个位置分别绑定旧列表三个位置上的元素的内存地址。

# 情况1:修改旧列表位置0上的元素,
list1[0] = 10
# 这条语句会将list1位置0上存储的内存地址改为数字10的内存地址,此时list2[0]上保存的还是位置0的内存地址。


# 情况2:修改旧列表位置2上子列表内的元素,
list1[2][0] = 110
# 这条语句会将子列表位置0上的保存的内存地址从数字11的内存地址修改为数字110的内存地址。
# 此时list1和list2的位置2上保存的都是子列表的内存地址。
# 通过list1[2]和list2[2]访问到的是同一块内存地址,访问到的数据是一样的,访问的都是‘瓶子’。所以修改后,list2[2][0] == 110,

# 如果我们想实现,修改list1[2][0] = 110后, list2[2][0] 还是等于11
# 则必须使用深拷贝

深拷贝

import copy			# 导入内置copy模块
list1 = [1, 2, [11, 22]]
list2 = copy.deepcopy(list1)	# 调用deepcopy()实现深拷贝

图3是描述列表深拷贝操作,计算机内存中发生的变化。

我们知道浅拷贝,不区分对待元素的是不是可变还是不可变的数据类型。新拷贝的列表的三个位置分别绑定旧列表三个位置上的元素的内存地址。

对于深拷贝区别对待其元素的可变和不可变数据类型。当元素是不可变数据类型时,直接绑定旧列表对应位置上的元素的内存地址;当元素是可变数据类型时,会重新开辟一块内存空间,存放这个元素对象的内存地址,且该元素内部的可变数据类型也会再重新开辟内存空间,层层检测


补充总结

  • 深浅拷贝讨论的拷贝对象是可变数据类型;
  • 深浅拷贝的区别在于:是否区分元素的可变还是不可变类型的判断;
  • 需要有变量定义的基本知识支撑;
  • 列表的切片也是浅拷贝,copy模块的copy.copy()函数是浅拷贝操作,和列表的内置方法copy()功能相同。
posted @ 2020-03-06 22:19  the3times  阅读(198)  评论(0编辑  收藏  举报