详细讲解python深拷贝、浅拷贝、引用

很多初学者或者仅是浅尝辄止的使用过python的人都会分不太清什么是python的深浅拷贝和引用,今天我们就来聊聊这个事情。

我们通过几个问题来阐述一下。

第一个问题,python为什么会有深拷贝和浅拷贝?

  其实这个问题更通俗一点的问法就是:拷贝为什么还分深浅?尤其是还在python里面分了深浅,那是不是就意味着python中有什么东西是分层级的,会有好多层。为什么我会说有好多层呐,是因为如果只有一层的话就不会分什么深浅了嘛,对不对?所以我们就引出了第二个问题。

第二个问题python中有什么东西是可以分层级的?

  解答这个问题之前我们先来复习一下python都有几种基础的数据结构。一共是6种。

 # 1. 数字(包括:整形int,浮点型float,布尔型boolean)
int_a = 1
float_a = 1.1
bolean = True # python中True是数字,可以参加运算。True+1结果是2.False+1结果是1。
 # 2. 字符串 str 例如
 str_a = "hello"
 # 3. 列表 list [] 例如
 list_a = ['hello', 1,  {"a":"This is a dictionary"}, (2,),  {"This is a collection"},  [1, 2, 3]]
 # 4. 字典 dict {} ,键必须是不可变类型,值可以任意类型
 dict_a = {"key":"value", "list_key":[1, 2, 3]}
 # 5. 元组 tuple (1,)
 tuple_a = (1, 2, 3, [4, 5])
 # 6. 集合 set {}
 set_a = {"This is a collection", [1, 2, 3]}

  通过上述我们可以看到,除了数字类型和字符串类型,其余四种都是可以有层级的,列表,字典,元组,集合,这四种我们也可以称之为“容器”,这四种都可以在内部内嵌容器,于是这就有层级,这时就会有了深浅之分,只有一层就是浅,一层以上我们就可以称之为深了。
接下来我们看第三个问题

第三个问题,什么是拷贝,以及什么是引用?

  拷贝也叫做复制,也就是说,只要经过拷贝,就必须出现两个一样的东西或者我们称之为对象更好。无论是深拷贝还是浅拷贝,最终都会在内存中开辟出一块空间,来存储拷贝出来的对象,只不过深拷贝和浅拷贝在内存中开辟出来的空间存储的东西不一样,但是拷贝一定是有一个新的东西出现,会在内存中开辟一个地方来存储
  接下来说说引用:引用就是把一个对象绑定给一个变量名,当开发者使用这个变量名的时候,真正参与计算机运算(执行代码就是运算,不一定是加减乘除,还有打印,插入等操作都是运算。)的就是变量名绑定的对象,也叫作变量名指向的对象。但是一个对象可以绑定给多个变量名,也可以称之为被多个变量名指向。接下来上代码说明一下。

#  我们创建一个字符串对象绑定给变量名a。
a = "我被变量名a指向了,也叫作我和变量名a绑定在一起了"
# 再将a绑定给变量名b,但是此时此刻真正参与运算的是变量名a绑定的上面的那个字符串对象。
b = a
print("变量名a绑定的对象在内存中的地址:{}".format(id(a)))
print("变量名b绑定的对象在内存中的地址:{}".format(id(b)))
# 结果如下:(每个人执行的结果地址不一定一样,我们重点看现象,主要是看id(a)和id(b)是否相等
# 变量名a绑定的对象在内存中的地址:1754302936368
# 变量名b绑定的对象在内存中的地址:1754302936368

通过上述代码我们就可以很清晰的看到变量名a和变量名b绑定的是同一个对象,也就是说当出现一个变量名在等号右侧,然后一个变量名在等号左侧这种情况的时候,就是引用了,而这种情况下我们要知道的重点就是引用不会再内存中开辟一块空间来存储一个新的对象,而是给原来的对象增加一个绑定关系,所以这就是拷贝和引用的区别了。
接下来我们来看看深浅拷贝:

# python中,如果想要实现拷贝,必须引入一个copy的库。这是一个标准库,也就是说你安装了python解释执行器以后自带的这个库。
import copy
# 前文说了,深浅拷贝的区分首先要有层级,才会有深浅之分,所以我们这里面就拿列表list举例了。
list_a = [1, 2, 3, [4, 5]]
# 使用copy.copy()方法来浅拷贝一个对象b,我们来看看list_a和list_b这两个变量所绑定的对象的id值是不是一样的。
list_b = copy.copy(list_a)
#使用copy.deepcopy()方法来深拷贝一个对象c,让我们看看a b c这三个变量名所绑定的对象的id值是不是都一样。
list_c = copy.deepcopy(list_a)
print("变量名list_a绑定的对象在内存中的地址:{}".format(id(list_a)))
print("变量名list_b定的对象在内存中的地址:{}".format(id(list_b)))
print("变量名list_c定的对象在内存中的地址:{}".format(id(list_c)))
# 结果如下(每个人执行的结果地址不一定一样,我们重点看现象,这几个变量名所绑定的对象的地址是不是不相等)
# 变量名list_a绑定的对象在内存中的地址:1754342018880
# 变量名list_b定的对象在内存中的地址:1754303134016
# 变量名list_c定的对象在内存中的地址:1754348884480

通过上述代码以及说明,我们可以看到list_a, list_b, list_c三个变量名所绑定的对象的id值是不一样的,这就证明了只要是拷贝,无论是深浅,内存都开辟出来一块空间存储了新生成的对象,所以才会id值不一样。但是这三个变量名所绑定的对象的值是一样的,都是[1, 2, 3, [4, 5]] 这样一个列表,因为list_blist_c都是通过list_a拷贝出来的。
刚刚说了深浅拷贝的相同之处,并用代码展示了,现在我们来看看不同之处。
具体通过代码来演示一下:

# 还是上述三个对象
list_a = [1, 2, 3, [4, 5]]
list_b = copy.copy(list_a)
list_c = copy.deepcopy(list_a)
# 现在让我们看看内层对象是否id是相同的
print("这是list_a绑定的对象内层id:{}".format(id(list_a[3])))
print("这是list_b绑定的对象内层id:{}".format(id(list_b[3])))
print("这是list_c绑定的对象内层id:{}".format(id(list_c[3])))
# 我们来看看结果:
# 这是list_a绑定的对象内层id:2452344680576
# 这是list_b绑定的对象内层id:2452344680576
# 这是list_c绑定的对象内层id:2452344733248

我们可以看到,通过浅拷贝出来的对象,虽然外层绑定的是两个不同的对象,但是内层指向的都是同一个对象,也就是说如果把list_a内层可变对象改变了,那么打印list_b也是改变之后的,因为它们两个变量名索引位置为3的地方指向的是同一个对象,而这个对象还是可变对象(这个是很重要的)。但是list_c就不会改变,因为list_c内层idlist_alist_b不一样。我们通过代码来看一下。
在这里插入图片描述

通过上述代码,我们其实只需要记住一点,就是我们所看到的变量名,或者是容器内的索引位置,都是指向真正的变量的(变量名和索引位只是一个标签),只要指向的是同一个变量,那么最后表现出来的形式或者参与运算的对象一定是同一个。就好像list_a的索引3号位的那个列表一样,虽然我们是通过list_a这个变量名,在我们的代码中告诉计算机来进行改变的,但是所有指向这个对象的变量名或者索引位所表现出来的都是变化后的,我们可以看到打印都是一样,因为指向的是同一个id地址,但是我们改变了list_a列表的索引1号位置,list_b是没有跟着改变的,因为1号位置是不可变对象,也就不存在深浅之分,就只是遵循拷贝的规则(也就是说拷贝之前的一个和拷贝出来的另一个,这两个对象是完全不相关的,互不影响的),只有涉及到python的可变对象(python中所有可变对象都是容器)也就是容器的时候,深浅拷贝就能看出来区别了。浅拷贝的内层没有复制出来一个新的对象,但是深拷贝不管多深一定是依次复制出来一个新的对象。这也就是为什么list_a的内层变了,而list_b的也变了,但是完全没有影响到list_c.

以上就是我们所讲解的python的深拷贝和浅拷贝,还有引用了。如还有有什么不懂,或者有指正的,请在下方留言,看到我会回复。谢谢!

祝工作学习顺利!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值