Python中的变量是引用型变量,变量的内存空间中并没有存放真正的值,而只是存放了这个值对应的内存地址。当我们访问变量时,会获取变量中的内存地址,再通过内存地址获取其中的值。Python中的赋值语句是从右向左运行的,也就是先创建具体的值,在将值分配给对应的变量。
为了便于理解,可以将变量理解成便利贴,我们通常会在已存在的物品上贴上便利贴。
来看以下这段代码:
In [1]: l1 = [1,2,3,[4,5]]
In [2]: l1
Out[2]: [1, 2, 3, [4, 5]]
In [3]: l2 = l1
In [4]: l2
Out[4]: [1, 2, 3, [4, 5]]
In [5]: l1.append(6)
In [6]: l1
Out[6]: [1, 2, 3, [4, 5], 6]
In [7]: l2
Out[7]: [1, 2, 3, [4, 5], 6]
可以发现我们将l1赋值给l2,在给l1添加一个元素6后,l2中也同样添加了6,为什么会这样呢?
因为变量是便利贴,多个变量都贴在同一个对象上所以修改时改的是同一个对象。
这里我们可以通过id和代码运行过程可视化来观察验证:
In [8]: id(l1)
Out[8]: 2568435079752
In [9]: id(l2)
Out[9]: 2568435079752
#可以发现两个变量的id是相同的,证明修改的是同一对象
如果我们想将一个变量赋值给另外一个变量,然后修改新变量的时候又不影响原变量,应该怎么解决呢?
解决办法就是使用浅拷贝或深拷贝,需要导入copy
库
Python在进行浅拷贝时,会在内存中开辟一块新的空间并使用这个空间来存放被拷贝对象。
代码示例:
In [10]: import copy
In [11]: l1 = [1,2,3,[4,5]]
In [12]: l2 = copy.copy(l1)
In [13]: l1.append(6)
In [14]: l1
Out[14]: [1, 2, 3, [4, 5], 6]
In [15]: l2
Out[15]: [1, 2, 3, [4, 5]]
#可以发现在进行浅拷贝后修改l1并不影响l2
id验证:
In [16]: id(l1)
Out[16]: 2568435571528
In [17]: id(l2)
Out[17]: 2568435367752
#可以观察到现在两个变量的id是不同的
过程可视化:
可以观察到,浅拷贝开辟了新的空间去存储对象
观察下面的代码:
In [10]: import copy
In [11]: l1 = [1,2,3,[4,5]]
In [12]: l2 = copy.copy(l1)
In [13]: l1.append(6)
In [14]: l1
Out[14]: [1, 2, 3, [4, 5], 6]
In [15]: l2
Out[15]: [1, 2, 3, [4, 5]]
In [16]: id(l1)
Out[16]: 2568435571528
In [17]: id(l2)
Out[17]: 2568435367752
In [18]: l1[3].append(666)
In [19]: l1
Out[19]: [1, 2, 3, [4, 5, 666], 6]
In [20]: l2
Out[20]: [1, 2, 3, [4, 5, 666]]
浅拷贝虽然会开辟一块新的空间储存拷贝对象从而避免两者相互影响,但如果浅拷贝对象中有可变数据类型,那么这个可变数据类型还是会相互影响,怎么办?
使用深拷贝
代码示例:
In [23]: l1 = [1,2,3,[4,5]]
In [24]: l2 = copy.deepcopy(l1)
In [25]: l1.append(6)
In [26]: l1[3].append(666)
In [27]: l1
Out[27]: [1, 2, 3, [4, 5, 666], 6]
In [28]: l2
Out[28]: [1, 2, 3, [4, 5]]
过程可视化:
观察下列代码:
In [29]: l1 = (1,2,3)
In [30]: l2 = l1
In [31]: l3 = copy.copy(l1)
In [32]: l4 = copy.deepcopy(l3)
#更改l1
In [34]: l1 = (4,5,6)
In [35]: l1
Out[35]: (4, 5, 6)
#其他变量没变化
In [36]: l2
Out[36]: (1, 2, 3)
In [37]: l3
Out[37]: (1, 2, 3)
In [38]: l4
Out[38]: (1, 2, 3)
In [39]: l1 = [1,2,(3,4)]
In [41]: l2 = l1
In [42]: l3 = copy.copy(l1)
In [43]: l4 = copy.deepcopy(l3)
In [44]: l2
Out[44]: [1, 2, (3, 4)]
In [45]: l3
Out[45]: [1, 2, (3, 4)]
In [46]: l4
Out[46]: [1, 2, (3, 4)]
#更改l1
In [48]: l1 = (5,6)
In [49]: l1
Out[49]: (5, 6)
#其他变量没变化
In [50]: l2
Out[50]: [1, 2, (3, 4)]
In [51]: l3
Out[51]: [1, 2, (3, 4)]
In [52]: l4
Out[52]: [1, 2, (3, 4)]
过程可视化:
可以观察到三种拷贝方式效果都是一样的。
原因是浅拷贝与深拷贝只会对可变对象创建新的内存空间来存放数据,如果对象是不可变对象或其中存在不改变元素则直接沿用之前的,此时浅拷贝、深拷贝与普通赋值操作没有差别,核心点在于不可变对象不可被改变,所有使用什么方式赋值都无所谓。