【Python成长之路】快速理解复制、浅拷贝、深拷贝

 

哈喽大家好,我是鹏哥。

今天想聊聊的主题是 —— 浅拷贝/深拷贝

 

~~~上课铃~~~

 

赤伶HITA - 赤伶

1

写在前面

浅拷贝、深拷贝的知识点,在python笔试和面试的过程中,经常被作为考题来考察应聘者的基本功。比如问你“请简单阐述下浅拷贝、深拷贝之间的差异”,或者给你一个变量分别进行复制、浅拷贝、深拷贝来求新值。

因此,我就简单把自己的理解记录下,希望对部分初学者有所帮助。

2

示例代码

在进行示例代码展示前,我们先理解下什么叫 复制、浅拷贝、深拷贝。

【直接赋值】:其实就是对象的引用(别名)。 

【浅拷贝(copy)】:拷贝父对象,不会拷贝对象的内部的子对象。 

【深拷贝(deepcopy)】:copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。

好吧,反正我开始了解这些时对这 么官方的介绍是看不懂的,完全不知道是 什么意思。

Talk is cheap, Show me the code!

a = 1# 复制b = a# 浅拷贝c = copy.copy(a)# 深拷贝d = copy.deepcopy(a)

在进行介绍复制、浅拷贝、深拷贝前,我们还要了解下可变对象(dict/list/set)、不可变对象(int/str/float/tuple)。

 

【知识点1】可变对象、不可变对象

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

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

a = 1print(id(a), a)a = 2print(id(a), a)A = [1]print(id(A), A)A.append(2)print(id(A), A)A = [1,2]print(id(A),A)
结果:8791349191504 18791349191536 234759304 [1]34759304 [1, 2]34759368 [1, 2]

从上述代码可以看到,对不可变对象a进行值改变时,a的地址也发生了变化;但对可变对象A进行值改变时,内存地址是不变的,仍然是34759304。(这里注意,A=[1,2]是重新赋值,所以是新地址,可以理解成是A‘,已不再是A)

 

【知识点2】对不可变对象进行复制、浅/深拷贝

对不可变对象,复制/浅拷贝/深拷贝都是引用原对象的内存地址。

import copy
a = 1print(id(a), a)print(id(a), a)# 复制b = a# 浅拷贝c = copy.copy(a)# 深拷贝d = copy.deepcopy(a)a = 2print(id(a), a)print(id(b), b)print(id(c), c)print(id(d), d)
结果:8791347356496 1  -- 变化前的a8791347356528 2  -- 变化后的a8791347356496 1  -- 复制8791347356496 1  -- 浅拷贝8791347356496 1  -- 深拷贝

因此,对于不可变对象,如果原对象发生什么变化,复制/浅拷贝/深拷贝都不会跟着变。就像对于一个又没有上进心又穷的小伙子,无论什么女生都不会跟着他过一生,这男的要怎么样就怎么样吧,没人管他。

 

【知识点3】对可变对象进行复制、浅/深拷贝​​​​​​​

import copy
a = [1, 2, [3, 4]]print(id(a), a)# 复制b = a# 浅拷贝c = copy.copy(a)# 深拷贝d = copy.deepcopy(a)a[1] = 5a[2].append(6)a.append(7)print(id(a), a)print(id(b), b)print(id(c), c)print(id(d), d)
结果:43241288 [1, 2, [3, 4]]          -- 变化前的a43241288 [1, 5, [3, 4, 6], 7]    -- 变化后的a43241288 [1, 5, [3, 4, 6], 7]    -- 复制43311048 [1, 2, [3, 4, 6]]       -- 浅拷贝43310984 [1, 2, [3, 4]]          -- 深拷贝

复制:原对象怎么变,我跟着变。另外,这里在可以发现对于可变对象,a变化前后的内存地址是不变的;就像老人们常说的“嫁鸡随鸡,嫁狗随狗”。

 

浅拷贝:原对象的外层元素地址变化,内层元素的地址不变。因为浅拷贝是会拷贝原对象的父对象,因此只有外层元素的内存地址发生了变化,因此通过a.append(7)改变外层元素,即外层的列表时,对浅拷贝是不影响的;

那这里你会肯定会问,为什么a[1]=5不变,而a[2].append(6)又发生了变化?因为a[1]是不可变对象,a[2]是可变对象。前面已经提过了,对于不可变对象是不会跟着变的。对于可变对象的子对象变了,浅拷贝是会跟着变的。

就像上面提到的又没有上进心又穷的小伙子,如果只是改变外观(外层元素地址变了)或者变得有钱(穷属性是不可变对象),是不会挽回前女友(浅拷贝)的心,只有改变了上进心(上进心属性是可变对象),前女友才会回心转意而发生变化。

当然如果a[2]是通过a[2]=[3,4,6]来改变的话,浅拷贝是不会跟着变化的。原理已经在第1个知识点里讲过了,这里不再复述。你可以理解成小伙子完全换了颗心脏,都不再是原先那个心脏了。(好吧,这个比方有点牵强。)

 

深拷贝:原对象的外层/内层元素地址都变化。这个就好理解了,前女友对你彻底死心,无论你怎么变都不要你了。

 

综上所述,复制相当于是无论什么条件都愿意跟着你结婚的好女孩;浅拷贝相当于有机会回到你身边的前女友,但要看你表现;深拷贝是完全对你死心的前女友。(和面试官可别这么说哈!)

3

补充点

如果对复制/浅拷贝/深拷贝的对象进行改变,那原对象会怎么变呢?​​​​​​​

import copy
a = [1, 2, [3, 4]]print(id(a), a)# 复制b = a# 浅拷贝c = copy.copy(a)# 深拷贝d = copy.deepcopy(a)b[0] = 9print(id(a), a)c[1] = 10c[2].append(11)print(id(a), a)d[2].append(12)print(id(a), a)
结果:43241416 [1, 2, [3, 4]]  -- 原对象a43241416 [9, 2, [3, 4]]   -- 对复制进行改变后的a43241416 [9, 2, [3, 4, 11]]  --对浅拷贝进行改变后的a43241416 [9, 2, [3, 4, 11]]  -- 对深拷贝进行改变后的a

我想大家应该能理解c[1] = 10为什么不改变原对象,而c[2].append(11)能改变原对象的原理吧 ?

4

总结

我觉得,理解复制/浅拷贝/深拷贝的关键还是要看对应外层元素/子层元素的地址有没有变化。地址变了,那就完全是新的对象;如果地址不变,那地址所对应的值变化,也就跟着变化了。另外,还要看原对象是可变对象还是不可变对象。

 

~~~下课铃~~~

 

【往期热门文章】:

【Python成长之路】10行代码教你免费观看无广告版的《庆余年》腾讯视频

【Python成长之路】如何用python开发自己的iphone应用程序,并添加至siri指令

【Python成长之路】从 零做网站开发 -- 基于Flask和JQuery,实现表格管理平台

 

点击下方诗句,可以留言互动喔  

四方食事,不过一碗人间烟火。万千得失,不过一段烛光消逝。

 

【关注“鹏哥贼优秀”公众号,回复“python学习材料”,将会有python基础学习、机器学习、数据挖掘、高级编程教程等100G视频资料,及100+份python相关电子书免费赠送!】

 

扫描二维码

    与鹏哥一起

学python吧!

​​​​​​​

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值