复制的"玄学"
为什么说复制是玄学呢?其实如果你是编程小白,在学习 Python 或者任何语言的时候, 都会遇到这个问题,我既然是复制了一个复制品,为什么我改动这个复制品,原物也会发生改变呢? 如果我没有对内存机制的一个理解,这件事情对于我就是一门玄学
。
好在我今天就帮对程序中的复制还不太清楚的同学们,解释一下你在复制时,究竟复制的是什么?
注:不同的语言中,对复制有着不同的定义,但是都离不开复制地址和复制值这两种说法。我们下面就来具体说明吧。
我的确复制了呀¶
先看看下面这种复制,为了验证我的确是复制了,我们使用列表的复制功能, 先复制一份列表 l
,存储在 _l
中。然后修改复制后的数值,看看源列表有没有同样被修改?
从结果看出,我们的确复制了列表里所有的值,对复制后的列表的修改,没有影响到源列表。但其他时候也一样么?
其实你就搞了个隐分身¶
下面我们在用列表把列表里再放一个列表,copy()
源列表 l
去 _l
。这次对 _l
里边的小列表修改,看看源列表是什么变化。
当你运行后,你会发现,源列表居然变了!不明真相的我认为这是玄学! 明明都复制了,为什么还会关联到源列表?难不成我用的是量子计算机,有量子纠缠??
我们再来看看另外一种情况,我把类别的实例存放到 l
列表中,然后 copy 再修改,看看什么情况?
明明源列表是 mp3
的,怎么被复制后的列表改变成了 mp4
了,自动升了个级?查阅 Python 的说明书
,你就会发现, 原来在 Python 中复制东西,有两种方式,一种是深拷贝,一种是浅拷贝。
深拷贝与浅拷贝¶
说英文可能会更加好理解一点,深拷贝是 Deep Copy,浅拷贝是 Shallow Copy。 Deep copy 就是我们通常意义上的复制,把东西全部再造
了一遍,彻底成为了两个独立的个体。 而 Shallow Copy, 其实也有一点影子拷贝
的意思,我复制出来的是你的一个影子,一个投影成像。 所以真实的实体是没有被复制的,我只复制了这个实体的一个投影而已。
那么在上面的案例中,比如列表中的列表,列表中的 Class 实例,其实都是复制了他们的投影,没有动到真身。 想要让投影改变,那就必须动到真身。所以在修改 _l
中的拷贝时,它就回去找真身了,那么真身就会被改变了。
但为什么复制数值的时候,源列表中的数值没有改变呢?难道数值没有真身? 这可能就有点玄学了,哈哈。Python 他在创造之初,就有这么个约定,列表中直接存放的数值,字符,和存 class 实例,列表,字典不同。 对数值字符的复制,直接是复制的值,而不是一个投影。
那么问题来了,如果我想做对存放实例的列表做 Deep Copy 深拷贝的时候咋办?
Python 自带了一个 copy 的库,里面就有一个 deepcopy
,用这个你就能实现完全的深拷贝。上面尝试了深拷贝列表当中的列表。 你也可以尝试将我上面那个 class 复制的代码贴上,用 deepcopy
来试试,看看会怎样?
总结¶
这就是 Python 比较玄学的地方。之后我们学习到 Python 科学计算库 Numpy 的时候,这种类似的玄学
又会从天而降。如果你没弄懂这些, 有时候出错了都不知道是哪错了,其实很有可能就是你浅复制后,改变了不该被改变的值。
而且我们我们也会讲,浅复制的优势就是快。复制是需要内存和时间的,因为浅复制没有真正复制,所以并不需要耗多少内存和时间, 所以浅复制也是有优势的,一个字快
。