python深浅拷贝的深度解析(内存/地址)
前置知识
1.python没有指针, 可以通过内置函数id来查看内存地址
2.python中list存放的是地址, 而非元素本身, 地址是按顺序存放的
3.python容器类型list有两种地址, 一个是外层对象/父对象地址, 一个是每个元素/子对象的地址
4.当修改list中的不可变对象时, 对象会发生拷贝copy-on-write, list存放的地址会发生变化, 这点可以保证任何拷贝都无需拷贝不可变对象本身, 只需要拷贝地址即可
import copy
a = [1, 2, [3, 4]]
b = a
c = a.copy()
d = copy.deepcopy(a)
# 测试
assert id(a) == id(b) and id(a[0]) == id(b[0]) and id(a[2]) == id(b[2])
assert id(a) != id(c) and id(a[0]) == id(c[0]) and id(a[2]) == id(c[2])
assert id(a) != id(d) and id(a[0]) == id(d[0]) and id(a[2]) != id(d[2])
a[0] = 5
assert id(a) == id(b) and id(a[0]) == id(b[0]) and id(a[2]) == id(b[2])
assert id(a) != id(c) and id(a[0]) != id(c[0]) and id(a[2]) == id(c[2])
assert id(a) != id(d) and id(a[0]) != id(d[0]) and id(a[2]) != id(d[2])
a[2].append(6)
assert id(a) == id(b) and id(a[0]) == id(b[0]) and id(a[2]) == id(b[2])
assert id(a) != id(c) and id(a[0]) != id(c[0]) and id(a[2]) == id(c[2])
assert id(a) != id(d) and id(a[0]) != id(d[0]) and id(a[2]) != id(d[2])
这三种"拷贝"方式有各自的含义
1.b = a
b是a的一个引用, 不存在任何对象和地址拷贝, 即b只是a的一个别名
2.c = a.copy()
c拷贝了a的外层对象, 内层对象的地址
当a中的不可变对象发生变化, a中的地址变化(参照前置知识4), c中的地址不变, 体现为c中的元素不跟随a变化
当a中的可变对象发生变化, a中的地址不变, c中的地址不变, 体现为c中的元素跟随a变化
3.d = copy.deepcopy(a)
d拷贝了a的外层对象, 内层可变对象, 内层不可变对象的地址(参照前置知识4), 与a完全独立,
总结: 深浅拷贝的区别在于拷贝的是内层可变对象的地址还是内层可变对象本身(对于不可变对象来说, 永远只需要拷贝地址)
如有疏忽错误,
欢迎批评指正!