python中的深浅拷贝_Python中的深浅拷贝

在讲深浅拷贝之前,我们先重温一下 is 和==的区别。

在判断对象是否相等比较的时候我们可以用is 和 ==:

is:比较两个对象的引用是否相同,即它们的id是否一样。

== : 比较两个对象的值是否相同。

id() ,是Python的一个内置函数,返回对象的唯一标识,用于获取对象的内存地址。

如下:

a = 1b= 1print(id(a)) #2040055728print(id(b)) #2040055728print(ais b) # True

首先,会为整数1分配一个内存空间。 变量a 和 b 都指向了这个内存空间(内存地址相等),所以他们的id相等。

即 a is b 为 True。

但是,真的所有整数数字都这样吗? 答案是:不是! 只有在 -25 ~ 256范围中的整数才不会重新分配内存空间。

如下所示:

因为257 超出了范围,所以id不相同,所以a is b返回的值为False。

>>> a = 257

>>> b = 257

>>>print(id(a))20004752

>>>print(id(b))20001312

>>> print(a isb)

False>>> print(a ==b)

True

这样做是考虑到性能,Python对-5 到 256 的整数维护了一个数组,相当于一个缓存, 当数值在这个范围内,直接就从数组中返回相对应的引用地址了。如果不在这个范围内,会重新开辟一个新的内存空间。

补充:大整数对象池。终端是每次执行一次,所以每次的大整数都重新创建,而在pycharm中,每次运行是所有代码都加载都内存中,属于一个整体,所以这个时候会有一个大整数对象池,即处于一个代码块的大整数是同一个对象。

is 和 == 哪个效率高?

相比之下,is比较的效率更高,因为它只需要判断两个对象的id是否相同即可。

而==则需要重载__eq__ 这个函数,遍历变量中的所有元素内容,逐次比较是否相同。因此效率较低。

浅拷贝和深拷贝

给变量进行赋值,有两种方法:直接赋值,拷贝。

直接赋值就 =就可以了。而拷贝又分为浅拷贝和深拷贝。

赋值

a = [1, 2, 3]

b=a

print(id(a)) #52531048print(id(b)) #52531048

定义变量a,同时将a赋值给b。打印之后发现他们的id是相同的。说明指向了同一个内存地址。

然后修改a的值,再查看他们的id

a = [1, 2, 3]

b=a

print(id(a)) #46169960a[1] = 0print(a, b) # [1, 0, 3] [1, 0, 3]

print(id(a)) #46169960print(id(b)) #46169960

这时候发现修改后的a和b以及最开始的a的内存地址是一样的。也就是说a和b还是指向了那一块内存,只不过内存里面的[1, 2, 3] 变成了[1, 0, 3]。

因为每次重新执行的时候内存地址都是发生改变的,此时的id(a) 的值46169960与52531048是一样的。

所以我们就可以判断出,b和a的引用是相同的,当a发生改变的时候,b也会发生改变。

赋值就是:你a无论怎么变,你指向谁,我b就跟着你指向谁。

拷贝

提到拷贝就避免不了可变对象和不可变对象。

可变对象:当有需要改变对象内部的值的时候,这个对象的id不发生变化。

不可变对象:当有需要改变对象内部的值的时候,这个对象的id会发生变化。

a = [1, 2, 3]

print(id(a)) #56082504a.append(4)

# 修改列表a之后 id没发生改变,可变对象

print(id(a)) #56082504a= 'hello'print(id(a)) #59817760a= a + 'world'print(id(a)) #57880072# 修改字符串a之后,id发生了变化。不可变对象

print(a) # hello world

浅拷贝

拷贝的是不可变对象,一定程度上来讲等同于赋值操作。但是对于多层嵌套结构,浅拷贝只拷贝父对象,不拷贝内部的子对象。

使用copy模块的 copy.copy 进行浅拷贝。

import copy

a= [1, 2, 3]

b=copy.copy(a)

print(id(a)) #55755880print(id(b)) #55737992a[1] = 0print(a, b) # [1, 0, 3] [1, 2, 3]

通俗的讲,我将现在的a 复制一份重新分配了一个内存空间。后面你a怎么改变,那跟我b是没有任何关系的。

对于列表的浅拷贝还可以通过list(), list[:] 来实现。

但是!我前面提到了对于多层嵌套的结构,需要注意。

看下面的例子

import copy

a= [1, 2, [3, 4]]

b=copy.copy(a)

print(id(a)) #23967528print(id(b)) #21738984# 改变a中的子列表

a[-1].append(5)

print(a) # [1, 2, [3, 4, 5]]

print(b) # [1, 2, [3, 4, 5]] ?? 为什么不是[1, 2, [3, 4]]呢?

b是由a浅拷贝得到的。我修改了a中嵌套的列表,发现b也跟着修改了?

如果还是不太理解,可以参考下图。LIST就是一个嵌套的子对象,指向了另外一个内存空间。所以浅拷贝只是拷贝了元素1, 2 和子对象的引用!

另外一种情况,如果嵌套的是一个元组呢?

import copy

a= [1, 2, (3, 4)]

b=copy.copy(a)

# 改变a中的元组

a[-1] += (5,)

print(a) # [1, 2, (3, 4, 5)]

print(b) # [1, 2, (3, 4)]

我们发现浅拷贝得来的b并没有发生改变。因为元组是不可变对象。改变了元组就会生成新的对象。b中的元组引用还是指向了旧的元组。

深拷贝

所谓深拷贝呢,就是重新分配一个内存空间(新对象),将原对象中的所有元素通过递归的方式进行拷贝到新对象中。

在Python中 通过copy.deepcopy()来实现深拷贝。

import copy

a= [1, 2, [3, 4]]

b=copy.deepcopy(a)

print(id(a)) #66587176print(id(b)) #66587688# 改变a中的可变对象

a[-1].append(5)

print(a) # [1, 2, [3, 4, 5]]

print(b) # [1, 2, [3, 4]] 深拷贝之后字列表不会受原来的影响

结语

1、深浅拷贝都会对源对象进行复制,占用不同的内存空间。

2、浅拷贝只能拷贝父目录,改动子目录时会影响浅拷贝的对象。

3、列表的切片本质就是浅拷贝。

转载:https://segmentfault.com/a/1190000038163495

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值