Python 深浅拷贝

Python 拷贝现象

先看一段代码:

>>> a = [1, 2, 3]
>>> b = a
>>> b.append(4)
>>> b
[1, 2, 3, 4]
>>> a
[1, 2, 3, 4]
>>>

代码的原意是,变量 b 从变量 a 处拷贝后,然后修改 b 而不改变原来的变量 a 的值;但事实是,原变量的值却变了!

问题现象所涉及到的点就是:Python 的对象赋值和深浅拷贝

在 Python 中赋值语句总是建立对象的引用值,而不是复制对象,这样看来 Python 变量更像是指针而不是数据存储区域。

继续看如下代码:

>>> a = [1, 2, 3]
>>> a[1] = a
>>> a
[1, [...], 3]
>>>

这段代码的本意是想给 a[1] 重新赋值为 [1, 2, 3],但是却死循环了([...]代表死循环)。

因为前面说过,Python 里没有复制对象,而是建立对象的引用值,即是类似指针操作,所以前述代码即是自己指向自己的指针,所以会导致死循环。

Python 里没有变量,通常说的变量是一种标签,是引用,类似指针。

Python 深浅拷贝

前面的示例代码中,为了得到预期的 [1, [1, 2, 3], 3] 结果,就不能直接将 a[1] 指向 a 引用的对象本身,而是需要将先 a 对象本身复制出来成为一个新对象,然后再将 a[1] 指向这个新对象。

Python 里复制对象操作因对象类型而异,复制列表的操作为:

b = a[:]

接下来再看看一段示例代码:

>>> a = [1, 2, ['a', 'b']]
>>> b = a[:]
>>> a[0] = 100
>>> a[2][0] = 'xyz'
>>> a
[100, 2, ['xyz', 'b']]
>>> b
[1, 2, ['xyz', 'b']]
>>>

变量 b 现在是 a 的拷贝了,但是 b[2][0] 为什么却也变了呢?

这种方式用于生成对象的拷贝或复制序列,而不再是引用和共享变量,但是只能顶层复制,即浅拷贝

因为列表复制操作 a[:] 只是浅拷贝,内部深层对象并未拷贝,所以会变。

先来看看深浅拷贝的概念:

浅拷贝:只拷贝父对象,不会拷贝对象的内部的子对象。

深拷贝:拷贝对象及其子对象。

Python 的 copy 库提供了深浅拷贝的功能,如下示例:

>>> import copy
>>> a = [1, 2, 3, ['a', 'b', 'c']] # 原对象
>>> b = a # 赋值,传对象的引用
>>> c = copy.copy(a) # 对象拷贝,浅拷贝
>>> d = copy.deepcopy(a) # 对象拷贝,深拷贝
>>> a.append(5) # 修改对象a
>>> a[3].append('d') # 修改对象a中的子对象
>>> a
[1, 2, 3, ['a', 'b', 'c', 'd'], 5]
>>> b
[1, 2, 3, ['a', 'b', 'c', 'd'], 5]
>>> c
[1, 2, 3, ['a', 'b', 'c', 'd']]
>>> d
[1, 2, 3, ['a', 'b', 'c']]
>>>

综上,在 Python 里需要对对象进行拷贝时,需要弄清楚是深拷贝还是浅拷贝操作?

如果是浅拷贝则可以使用列表复制copy.copy()两种方式,如果是深拷贝则须使用copy.deepcopy()方法。

参考资料