python copy deepcopy_python中的深浅copy shallow copy和 deep copy

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 # 如果字典里已经存储了将要拷贝的对象,则直接返回

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值