3.6.3. 浅拷贝 与 深拷贝 理解

(浅层复制 与 深层复制 理解)

浅拷贝 shallow copy
深拷贝 deep copy

总结:

浅拷贝: 是指在复制过程中,只复制一层变量绑定关系,不会复制深层变量绑定的对象的复制过程
深拷贝: 对所有可变对象进行层层复制,实现对象的各自独立。

3.6.3.1. 语法及使用方法:

基本语法:

import copy   # 导入copy模块
L = "任意类型数据"
L2 = copy.copy(L)     # 浅拷贝
L2 = copy.deepcopy(L) # 深拷贝

特别的,在列表中还可以通过切片和自身的copy方法进行浅层复制,如下:

# 浅拷贝
# 1) 利用切片方法。切片——返回切片后的新对象。如
L2 = L[:]
L2 = L[::]
# 2) 利用列表自身方法
L2 = L.copy()

3.6.3.2. 原理理解

浅层复制只复制最外一层的数据或容器对象,内部直接被赋值绑定于新对象对应位置

示例:如何解释Out4和Out5相同,Out9和Out10部分相同,以及后面的测试结果?

In [1]: import copy

In [2]: L = [-10,  (-20, -30),   [-40, -50, (-60, -70)]]


# 浅拷贝测试
In [3]: X = L.copy()

In [4]: [id(x) for x in L]
Out[4]: [1684750713584, 1684750470920, 1684749580488]

In [5]: [id(x) for x in X]
Out[5]: [1684750713584, 1684750470920, 1684749580488]

In [6]: id(L), id(X)
Out[6]: (1684750584648, 1684750627144)


# 深拷贝测试
In [7]: Y = copy.deepcopy(L)

In [8]: [id(x) for x in L]
Out[8]: [1684750713584, 1684750470920, 1684749580488]

In [9]: [id(x) for x in X]
Out[9]: [1684750713584, 1684750470920, 1684749580488]

In [10]: [id(x) for x in Y]
Out[10]: [1684750713584, 1684750470920, 1684748555272]


# 数据测试
In [11]: L.append("测试1")

In [12]: L[2].append("测试2")

In [13]: L, X, Y
Out[13]: 
([-10, (-20, -30), [-40, -50, (-60, -70), '测试2'], '测试1'],
 [-10, (-20, -30), [-40, -50, (-60, -70), '测试2']],
 [-10, (-20, -30), [-40, -50, (-60, -70)]])

图解:

  1. 创建L列表
    创建3个数据对象,放入列表中(绑定于数据容器对应位置)
L = [-10,  (-20, -30),   [-40, -50, (-60, -70)]]

绑定关系如下:

# 索引(变量)绑定关系,索引也可看做特殊变量
      L[0]  L[1]           L[2]
       |     |             |
       |     |             |
L = [-10,  (-20, -30),   [-40, -50, (-60, -70)]]
  1. 对L浅拷贝绑定X
X = L.copy()
# 或
X = L[::]
# 或 其他...

绑定关系如下:

L[0]  L[1]           L[2]
        |     |            |
        |     |            |
L = [-10,  (-20, -30),   [-40, -50, (-60, -70)]]
        |     |            |
        |     |            |
      X[0]  X[1]           X[2]     
        |     |            |
        |     |            |
X = [                                          ]
# 1)重新创建最外层可变数据容器,浅拷贝创建新的最外层对象
# 2)内部所有对象正常赋值绑定
# 绑定原始对象关系,3个数据对象同时被L[N]和L[X]绑定

最外层可变数据容器重新在内存中另一区域开辟创建, 而内一层所有数据对象, 都只是在原来的基础上简单赋值,因而改变最外层新拷贝对象对原始对象无影响,而改变拷贝后的数据对象如X[2],将会使L[2]也同时发生改变

  1. 对L深拷贝绑定Y
    可以简单理解为所有的数据对象全部在内存中拷贝一份,并重新对新数据对象指定绑定关系。
    因而,深拷贝后的数据无论如何操作都将不会影响到之前的数据对象,即改变拷贝后的数据对象如Y[2],不会影响L[2]同时发生改变
    (不过深拷贝的实际情况为了节省计算机资源,只是复制和重新绑定了新的可变数据对象,而其中不可变的数据对象依然保留,重新赋值绑定,这样节省内存资源的同时亦达到了完全拷贝的同样效果。)

练习

练习1:

import copy  # 导入复制模块
L = [3.1, 3.2]
L1 = [1, 2, L]
L2 = copy.deepcopy(L1)  # 实现深拷贝
L[0] = 3.14
print(L1)  # _________
print(L2)  # _________

# [1, 2, [3.14, 3.2]] #此列表由于浅拷贝受影响
# [1, 2, [3.1, 3.2]] #此列表不受影响

练习2:

import copy

a = [1, 2, 3, 4, ['a', 'b']]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
a.append(5)
a[4].append('c')

print(a)  # _________
print(b)  # _________
print(c)  # _________
print(d)  # _________

# [1, 2, 3, 4, ['a', 'b', 'c'], 5]  
# [1, 2, 3, 4, ['a', 'b', 'c'], 5]  
# [1, 2, 3, 4, ['a', 'b', 'c']]     
# [1, 2, 3, 4, ['a', 'b']]
"""解析: 
> a: a加了5,a[4]加了'c'  
> b: 赋值绑定操作,与a完全一致  
> c: 浅拷贝了最外层可变对象,最外层不受影响,但内部可变对象受影响  
> d: 深拷贝,所有可变对象不受影响"""