我对于Python中身深浅贝的理解,以及概述Python中深浅拷贝中的大坑
今天说一说,自己对于python中 深浅拷贝的理解,以及深浅拷贝中,到底哪里存在大坑,此次写这篇博客,一方面是为了 复习一下前面的东西,另一个方面,深一步了解深浅拷贝。
今天需要解决的问题:
- 通俗理解深浅拷贝是什么?
- 深浅拷贝中有哪些大坑?
【1】问题一:怎么通俗理解深浅拷贝到底是什么?
我们先从名字上来看,顾名思义 深拷贝就是比较深刻的拷贝,就像是递归拷贝一样,拷贝的是整个 结构,整个空间中的数据;浅拷贝呢,也顾名思义的理解一下,浅拷贝嘛,只拷贝最上边的一层很浅的内容,就好像是 一个什么都不会的学生抄别人的数学作业一样,你只把作业抄上了,但是作业中包含的东西,这些数学题用了什么什么方法,怎么怎么做出来的,不去管,只把表层的东西复制一份出来,这就是浅拷贝。
为了更好的理解深浅拷贝,以及了解深浅拷贝的用法,下面我用几张图来具体的分析一下:
平时我们学习中,常见的一种简单的浅拷贝就是——赋值
这里,我定义了一个列表 a,把 a 赋值 给了 b,然后又通过 id 函数得知,a b 两个变量指向的内存空间相同,这说明一个什么事呢?这说明,在Python中,当运用赋值语句进行赋值时,实际上是 一个变量名指向整个内存空间。具体到实例中,看图:
a = [1, 2, 3]
实际上过程就是,开辟了一个内存空间,用来存放列表[1, 2, 3]
,然后把 a这个变量名 指向了 这个内存空间,而不是 开辟的空间叫 a。这里就好像是 把 变量名 a 和 列表 之间拉了一根绳子,你可以通过变量名 a,根据它的这根绳子找到 [1, 2, 3]。
b = a
这里怎么理解呢,你可以这样想,这句话你光看表面意思是 把 a 赋值给 b,上面说了,python中的赋值语句,实际上就是指向,这里光从表面来看,就是把 b 指向a嘛,但是,a不是一个内存空间,然而赋值语句指向,指向的是 内存空间。所以这里自然而然地可以想到,b 应该指向的是 a这个变量名所指向的内存空间。
所以 接下来的id(a)
和id(b)
数值一样,也就验证了a变量和b变量 实际上指向的是同一个内存空间。
这里需要说明的是 a = b,如果在其他语言(比如说 C 语言中)
这里赋值 指的就确确实实是 重新开辟一个和 b里面数据的一模一样的空间,这个空间的 名字叫做 a
深拷贝怎么用?
首先,你需要先导入
copy
模块,调用 它里面的deepcopy
方法,这个方法,就可以实现 深拷贝。
下面,为了更深刻的 理解深拷贝,我画了一张图,看图:
首先a = [2, 3, 4]
,表示赋值,上面我们说了,python中所有的赋值操作都是 指向,也就是说,这句话意思是 开辟了一个内存空间 存放了列表[2, 3, 4]
,然后把 a 指向了这个空间。
接下来,我又调用了copy 模块
,执行deepcopy
方法,在这一步b = copy.deepcopy(a)
,我们可以这样理解:通过调用deepcopy
方法,实际上又开辟了一个空间,这个空间中的数据,是 a变量名指向的空间里面的数据,然后再把 b变量名指向 这个空间。重要的是,看图理解!!!
问题二:深浅拷贝中有哪些大坑?
首先,是关于 copy
模块中的 copy
的,我画了一张图,有助于理解:
copy函数中的 copy方法达到的浅拷贝:
猛的一看这张图,吓一跳,这是什么?怎么这么复杂?你TM在表达什么?
先别害怕,不难,真的不难,下面我就讲讲,到底什么意思,到底怎么简单的去理解?
还是首先 开辟两个空间[1, 2, 3]
和[4, 5, 6]
,然后,把 这两个变量名 a 和 b 分别指向这两个内存空间
接下来,有的人可能就蒙了,你说这个c = [a, b]
这是什么意思呢?
来,我们看图,红色 横线以上,右半部分。
这里呢,表达的意思就是:c
这个变量名,指向了一个内存空间,这个内存空间里,又放着变量名a
和b
,这里面的a
变量名和b
变量名,又分别指向了内存空间[1, 2, 3]
和[4, 5, 6]
也就是说,这里做了两件事,第一件事
——开辟一个内存空间,这个内存空间里放进去变量名 a 和 变量名b。第二件事
——把 c这个变量名,指向这个空间。这一步也是,看图理解!!!
下面,就是要进行浅拷贝实验了
首先,导入copy模块(import copy
),然后用这个模块里面的copy
方法进行浅拷贝(d = copy.copy(c)
),这里怎么理解呢,看图(红色横杠分界下半部分):
d = copy.copy(c)
这里时进行浅拷贝,这里,表面意思,我让程序 把c表面的内容
拷贝一份,然后把d
这个变量名指向 这个刚拷贝完的空间。上面也说了,浅拷贝嘛,光拷贝表面的。什么是表面的呢,这里,c
这个变量名表面的内容就是——c
指向了一个空间,这个空间里放着变量名 a 和 b。所以,程序就光把c
指向的空间里面两个变量名a
和b
,拷贝一份,放到另一个新开辟的空间中,然后再把d
这个变量名指向这个空间。这里,也是看图,理解!!!
好,这里,也说完了。
那坑到底在哪里呢?在哪里?快说
嗯。。。。。别急,我画个图给你看看这坑到底是什么样子的:
别的不说了,直接讲吧,看到这里看蒙了吧。
有的人会说了,哎哎哎,真么情况,上面不是说了吗,d = copy.copy(c)
不是重新开辟个空间吗,怎么又变成指向了?
这里,轻易i的那个看好了,这里的c
是元组
,不是列表
,可能现在读文章的哥们一愣:“我擦,什么?你TM到底有说的啥?”
这里,其实也很简单,为什么这里d = copy.copy(c)
是d
指向了c
变量名指向的内存空间,而不像上面说的d
又重新指向了一个新空间呢?这个坑,在于,c这个变量名指向的内存空间,是一个 元组,元组是不可变对象。这里,当调用copy.copy(c)
的时候,系统会自动判断,判断需要拷贝的变量名指向的空间为不可变对象,就不会进行拷贝,而是 指向。
接下来,还有一个大坑,在哪呢?在下面这张图里:
deepcopy中的坑:
我们还是看图说事——看上图,红色分割线往上,我们验证了一件事:deepcopy 对于空间里面纯数据的这种元组,不会拷贝,也是指向。原因也很简单,和上面那个坑一样,因为元组不可不对象。
我们这里,最关心的是分割线下面的部分。
这里,还是首先创建了两个空间,分别存放列表[88]
和 列表[99]
,然后把 变量名a
和变量名b
分别指向这两个空间。然后接下来,c = (a, b)
,这里,开辟了一个内存空间,这个空间里放着一个元组,元组里又有两个元素,分别是变量名 a 和变量名 b,然后a和b各自指向[88],[99],这里就不多说了,上面解释的已经很清楚了。接下来,使用 copy模块中的 deepcopy 拷贝 c,这里呢,确实是进行了深拷贝。
为什么呢?你不是前面说元组是不可变对象吗,执行深拷贝的时候应该不会拷贝啊,不应该只是指向嘛,怎么当执行 深拷贝的时候,就确确实实拷贝了整个数据空间呢?
这里,需要说的是,这个坑有一点需要注意:深拷贝 当拷贝的是 元素是纯数据的元组时,是指向,当 用深拷贝 拷贝的元组,只要元组中一个元素为可变的,那么 深拷贝就会拷贝整个结构。