『Python基础』第20节:深浅copy

一. 赋值运算

l1 = [1, 2, 'conan', [11, 22]]
l2 = l1

l1[0] = 111
print(l1)  # [111, 2, 'conan', [11, 22]]
print(l2)  # [111, 2, 'conan', [11, 22]]
print(id(l1[0]))  # 1872924208
print(id(l2[0]))  # 1872924208

l1.append(333)
print(l1)  # [111, 2, 'conan', [11, 22], 333]
print(l2)  # [111, 2, 'conan', [11, 22], 333]
print(id(l1))  # 2211423993544
print(id(l2))  # 2211423993544

l1[3].append(666)
print(l1)  # [111, 2, 'conan', [11, 22, 666], 333]
print(l2)  # [111, 2, 'conan', [11, 22, 666], 333]
print(id(l1[3]))  # 2211453800136
print(id(l2[3]))  # 2211453800136

​ 从上面的运行结果可以看到, 对于赋值运算来说, l1与l2指向的是同一个内存地址, 所以它们是完全一样的. 其中一个变量对列表进行改变, 剩下的变量在使用列表时, 就是使用的改变之后的列表.

赋值运算

二. 浅copy

# 同一代码块下
l1 = [1, 2, 'conan', [11, 22]]
l2 = l1.copy()

l1[0] = 111
print(l1)  # [111, 2, 'conan', [11, 22]]
print(l2)  # [1, 2, 'conan', [11, 22]]
print(id(l1[0]))  # 1872924208
print(id(l2[0]))  # 1872920688

print(id(l1[1]))  # 1872920720
print(id(l2[1]))  # 1872920720

l1.append(333)
print(l1)  # [111, 2, 'conan', [11, 22], 333]
print(l2)  # [1, 2, 'conan', [11, 22]]
print(id(l1))  # 2343138115272
print(id(l2))  # 2343138954824

l1[3].append(666)
print(l1)  # [111, 2, 'conan', [11, 22, 666], 333]
print(l2)  # [1, 2, 'conan', [11, 22, 666]]
print(id(l1[3]))  # 2343138954952
print(id(l2[3]))  # 2343138954952
# 不同代码块下
l1 = [1, 2, 'conan', [11, 22]]
l2 = l1.copy()

l1[0] = 111
print(l1)  # [111, 2, 'conan', [11, 22]]
print(l2)  # [1, 2, 'conan', [11, 22]]
print(id(l1[0]))  # 1872924208
print(id(l2[0]))  # 1872920688

print(id(l1[1]))  # 1872920720
print(id(l2[1]))  # 1872920720

l1.append(333)
print(l1)  # [111, 2, 'conan', [11, 22], 333]
print(l2)  # [1, 2, 'conan', [11, 22]]
print(id(l1))  # 2402040017736
print(id(l2))  # 2402040018120

l1[3].append(666)
print(l1)  # [111, 2, 'conan', [11, 22, 666], 333]
print(l2)  # [1, 2, 'conan', [11, 22, 666]]
print(id(l1[3]))  # 2402040003848
print(id(l2[3]))  # 2402040003848

对于浅copy来说, 只是在内存中重新开辟了一个空间存放一个新列表, 但是新列表里面的元素与原列表里面的元素的内存地址是同一个. 所以, l1与l2的id不同, 但是里面内容的id是相同的.

注意

我们发现, 在更改l1的第一个元素, 也就是把 1 改为 111 之后, 内存地址是不一样的, 这是因为int类型是不可变的数据类型; 而列表时可变的数据类型, 当里面的列表增加一个元素后, 其内存地址是不变的.

浅copy

三. 深copy

依旧是先看下代码

import copy
l1 = [1, 2, 'conan', [11, 22]]
l2 = copy.deepcopy(l1)

l1[0] = 111
print(l1)  # [111, 2, 'conan', [11, 22]]
print(l2)  # [1, 2, 'conan', [11, 22]]
print(id(l1[0]))  # 1872924208
print(id(l2[0]))  # 1872920688

print(id(l1[1]))  # 1872920720
print(id(l2[1]))  # 1872920720

l1.append(333)
print(l1)  # [111, 2, 'conan', [11, 22], 333]
print(l2)  # [1, 2, 'conan', [11, 22]]
print(id(l1))  # 2698658605000
print(id(l2))  # 2698660016776

l1[3].append(666)
print(l1)  # [111, 2, 'conan', [11, 22, 666], 333]
print(l2)  # [1, 2, 'conan', [11, 22]]
print(id(l1[3]))  # 2698659950152
print(id(l2[3]))  # 2698660016712

对于深copy来说, 深copy后的列表是在内存中重新创建的. 而且Python为了节省内存以及提高性能, 它做了这么一件事:

在深copy后的列表中,

如果其元素是可变的数据类型就再重新创建一个;

如果其元素是不可变的数据类型, 就公用一个, 也就是其内存地址是一样的.

在上面代码中的运行结果也能看出来.

深copy

四. 总结

浅copy: 嵌套的可变的数据类型 是 同一个.

深copy: 嵌套的可变的数据类型 不是 同一个.

五. 练习

5.1 练习1

写出下面代码的运行结果

l1 = [1, 2, 3, ['conan']]
l2 = l1[::]
l1[-1].append(666)
print(l1)
print(l2)

l1: [1, 2, 3, ['conan', 666]]

l2: [1, 2, 3, ['conan', 666]]

通过打印其id可以发现, l2 = l1[::] 的操作其实与浅copy一样.

posted @ 2019-08-13 21:23  李培冠  阅读(232)  评论(0编辑  收藏  举报