Python3中的赋值操作、浅拷贝与深拷贝
一:关于赋值操作与浅拷贝
1、变量的赋值
首先以一个例子来说明一下:定义变量a = 1,使b = a。开始的情况下打印a与b都为1(显而易见,哈哈)
但是接下来,我们做一下改动,令 a = 222,再打印a与b。
咦?说好的b = a呢?!怎么不一样了呢?
这里直接揭晓答案:
如上图所示,a = 1操作其实是将变量a 指向了数字1的内存地址,当进行b=a操作时,并不是将a的值直接赋值给b,而是直接把变量b指向了1的内存地址。当进行a = 222时,程序又将变量a指向了222的内存地址,而此时b指向的仍然是1的内存地址,因此最终打印出来的a=222,而b=1。
我们可以用下面方法来验证:
id()方法 可以得到变量以及其他数据类型的内存地址,从上图我们可以看到id(a)与id(b)相等,说明在进行b=a操作时,程序进行的是将变量b指向数字1的内存地址的操作。
这样就比较好理解了,当进行a = 222操作的时候,a指向了222的内存地址,而此时b仍然指向的是1的内存地址,所以打印出来的值也不一样了:
2、列表的赋值操作与浅拷贝
2.1列表的赋值:
这里我们还是以一个例子来进行说明:
一开始当然是一派和谐,嘿嘿!但是,当我们试图改变l1的时候,你会发现:
当我们将l1后面追加数字666后,打印出来的l1与l2竟然一样!这与上面的结果不同。为什么会是这样的呢?
列表与变量不同的是:当把列表[1,2,3,4,5]赋给l1的时候,l1指向的其实只是这个“列表”的内存地址,而列表中的每个元素是有自己独立的内存地址的,与这个列表无关。然后我们进行l2 = l1的操作时,程序执行的其实是“将l2指向了l1指向的列表的内存地址”。
因此,不管是l1还是l2,他们都只是指向了这个列表“容器”的地址,这个“容器”中有什么甚至做何种改变他们是不知道的。所以我们把列表后面追加数字666后,l1与l2指向的这个“容器”里装的“元素”是一模一样的~
我们还可以用id()进行验证:
这里可以看到l1与l2的内存地址一致。
2.2 列表的浅拷贝:
这里还是以一个例子开头:
这里当我们进行l2 = l1.copy()的浅拷贝操作时,我们会发现程序为l2新建了一个内存空间。然后我们再来进行改变操作:
当我们进行l1[3] = 666后,仅改变了l1里元素的值,l2仍然不变。这是因为列表中每个元素的内存地址是与列表本身的内存地址不同的,所以当l1与l2指向了不同的内存地址的列表的时候,改变l1中“元素”的值l2仍然不变。这里我们可以看看l1[3]与l2[3]的id就知道了:
可以看出l1[3]的内存地址与l2[3]的内存地址不同,因此改变l1[3]的值不会影响l2[3]。
但是如果我们换一个数据结构(列表嵌套列表):
这里我们将l1的l1[4]改成了一个列表,当把l1浅拷贝给l2后,在l1与l2中是将[5,6]这个整体列表当成了原列表中的“元素”来对待的,所以l1[4]的id与l2[4]的id是相同列表的id,当我们进行l1[4][0] = 666时,由于l1[4]与l2[4]指向的是相同的列表,所以打印出的值是一样的:
可以看出l1[4]的id与l2[4]的id是相同列表的id,由于内存地址相同所以改变其中的一个,另外的一个会跟着改变。
二:关于深拷贝
深拷贝,顾名思义当然是拷贝变量与原变量是独立存在的,这里直接上代码演示:
如上图所示,我们需要先导入copy模块来进行深拷贝,函数内用copy.deepcopy()方法进行实现。拷贝出来的列表是独立存在的个体。
但是深拷贝在用的时候需要注意,由于拷贝的变量是独立存在的,所以它也会占用内存空间。如果被拷贝的数据量很大(比如占2g),那么拷贝后的数据总量将翻倍(变成4g),用的时候先规划好空