python中copy是分为浅copy和深copy
shallow copy
重新分配一块内存,创建一个新的对象,里面的元素是被拷贝对象中子元素的引用。
- 特点:会创建新的对象,这个对象并非是原对象的引用,而是原对象内第一层子元素对象的引用。
import copy
# L1 对象内部为两个元素: index 0 :[1,2], index 1:(100,200)
L1 = [[1, 2], (100, 200)]
# 对L1进行浅copy ,此时,得到一个新的List对象,并赋值给L2,
L2 = list(L1)
print("L1的内存地址为--{},第一个元素的地址--{},第二个元素的地址--{}".format(id(L1), id(L1[0]), id(L1[1])))
print("L2的内存地址为--{},第一个元素的地址--{},第二个元素的地址--{}".format(id(L2), id(L2[0]), id(L2[1])))
# L1的内存地址为--2581953960968,第一个元素的地址--2581953961032,第二个元素的地址--2581951586568
# L2的内存地址为--2581953936904,第一个元素的地址--2581953961032,第二个元素的地址--2581951586568
# 修改共同引用的列表的内容,由于L1,L2的第一个元素都指向这个列表,因此,L1,L2 对应的元素内容 都发生了变化,但是id是不变的。
L1[0].append(3)
print(L1) # [[1, 2, 3], (100, 200)]
print(L2) # [[1, 2, 3], (100, 200)]
# L1 新增元素 ,L1 和L2 互相独立,不受影响
L1.append(100)
print(L1) # [[1, 2, 3], (100, 200), 100]
print(L2) # [[1, 2, 3], (100, 200)]
# 元组不可变,因此,元组使用 + ,得到的是一个新的元祖对象,因此L2内的元组对象id不变,L1内的元组对象id发生了变化。
L1[1] += (500, 600)
print(L1) # [[1, 2, 3], (100, 200, 500, 600), 100]
print(L2) # [[1, 2, 3], (100, 200)]
print("L1的内存地址为--{},第一个元素的地址--{},第二个元素的地址--{}".format(id(L1), id(L1[0]), id(L1[1])))
print("L2的内存地址为--{},第一个元素的地址--{},第二个元素的地址--{}".format(id(L2), id(L2[0]), id(L2[1])))
# L1的内存地址为--2581953960968,第一个元素的地址--2581953961032,第二个元素的地址--2581952542584
# L2的内存地址为--2581953936904,第一个元素的地址--2581953961032,第二个元素的地址--2581951586568
# 列表是可变的,+ 号操作,在之前基础上新增元素,类似列表的extend方法
L1[0] += [3000, 4000]
print(L1) # [[1, 2, 3, 3000, 4000], (100, 200, 500, 600), 100]
print(L2) # [[1, 2, 3, 3000, 4000], (100, 200)]
print("L1的内存地址为--{},第一个元素的地址--{},第二个元素的地址--{}".format(id(L1), id(L1[0]), id(L1[1])))
print("L2的内存地址为--{},第一个元素的地址--{},第二个元素的地址--{}".format(id(L2), id(L2[0]), id(L2[1])))
# L1的内存地址为--2581953960968,第一个元素的地址--2581953961032,第二个元素的地址--2581952542584
# L2的内存地址为--2581953936904,第一个元素的地址--2581953961032,第二个元素的地址--2581951586568
-
实现方式:
- 该对象类型的工厂函数,
- 切片操作(对部分类型对象有效)
- copy模块中的copy函数
L1=[[100,200],(1000,2000)]
L3=list(L1)
L4=L1[:]
L5=copy.copy(L1)
print("L3的内存地址为--{},第一个元素的地址--{},第二个元素的地址--{}".format(id(L3), id(L3[0]), id(L3[1])))
print("L4的内存地址为--{},第一个元素的地址--{},第二个元素的地址--{}".format(id(L4), id(L4[0]), id(L4[1])))
print("L5的内存地址为--{},第一个元素的地址--{},第二个元素的地址--{}".format(id(L5), id(L5[0]), id(L5[1])))
# L3的内存地址为--2581952584392,第一个元素的地址--2581953960904,第二个元素的地址--2581951626888
# L4的内存地址为--2581953960968,第一个元素的地址--2581953960904,第二个元素的地址--2581951626888
# L5的内存地址为--2581953936968,第一个元素的地址--2581953960904,第二个元素的地址--2581951626888
- 注意:对于元组,浅拷贝并不会创建新的内存,而是让新的变量指向被拷贝对象的内存地址。
s1=(100,200)
s2=tuple(s1)
s3=s1[:]
s4=copy.copy(s1)
print(id(s1)) # 2399341093128
print(id(s2)) # 2399341093128
print(id(s3)) # 2399341093128
print(id(s4)) # 2399341093128
deepcopy 深复制
重新分配一块内存,创建一个新对象,并将被拷贝对象中的所有元素,以递归的方式,复制到这个新对象中。新对象和原对象完全独立,互不影响。
import copy
L1=[[1,2],(100,200)]
L2=copy.deepcopy(L1)
print("L1的内存地址为--{},第一个元素的地址--{},第二个元素的地址--{}".format(id(L1), id(L1[0]), id(L1[1])))
print("L2的内存地址为--{},第一个元素的地址--{},第二个元素的地址--{}".format(id(L2), id(L2[0]), id(L2[1])))
# L1的内存地址为--1665896340552,第一个元素的地址--1665896340616,第二个元素的地址--1665893966024
# L2的内存地址为--1665896340488,第一个元素的地址--1665896340808,第二个元素的地址--1665893966024
L1[0].append(3)
print(L1) # [[1, 2, 3], (100, 200)]
print(L2) # [[1, 2], (100, 200)]
L1.append(1000)
print(L1) # [[1, 2, 3], (100, 200), 1000]
print(L2) # [[1, 2], (100, 200)]
- 注意:在这里,L1和L2中的元组的内存地址是一样的,但是并不影响, 因为元组属于不可变对象,无法进行其他操作来改变。可以共享。
- 特殊情况:
l1 = [[1, 2], (30, 40)]
l2 = copy.deepcopy(l1)
l1.append(100)
l1[0].append(3)
print(l1) # [[1, 2, 3], (30, 40), 100]
print(l2) # [[1, 2], (30, 40)]
这个例子,列表 x 中有指向自身的引用,因此 x 是一个无限嵌套的列表。但是深度拷贝 x 到 y 后,程序并没有出现 stack overflow 的现象。这是因为深度拷贝函数 deepcopy 中会维护一个字典,记录已经拷贝的对象与其 ID。拷贝过程中,如果字典里已经存储了将要拷贝的对象,则会从字典直接返回。
def deepcopy(x, memo=None, _nil=[]):
"""Deep copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
"""
if memo is None:
memo = {}
d = id(x) # 查询被拷贝对象x的id
y = memo.get(d, _nil) # 查询字典里是否已经存储了该对象
if y is not _nil:
return y # 如果字典里已经存储了将要拷贝的对象,则直接返回
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。