Python浅拷贝-深拷贝 解析

浅拷贝

copy.copy()

copy函数是浅拷贝,只对可变类型的第一层对象进行拷贝,对拷贝的对象开辟新的内存空间进行存储,不会拷贝对象内部的子对象

特性: 浅拷贝只会对可变类型第一层进行拷贝;

  • 可变类型:只能作用于列表/字典/集合,而不能拷贝数字/字符串/元组;
  • 第一层:例如一个列表内的嵌套列表无法拷贝[1,2,[a,b],3]。
如何验证?

通过id()对比拷贝前后对象的内存地址,或直接用is方法判断拷贝前后对象内存地址是否相同。

验证示例:
  • 字符串浅拷贝

    import copy
    a = "abc"
    b = copy.copy(a)
    print("a的内存地址:",id(a))
    print("b的内存地址:",id(b))
    

    输出:

    a的内存地址: 2140603563288 
    b的内存地址: 2140603563288
    

    结论:未拷贝

  • 数值浅拷贝

    a = 12
    b = copy.copy(a)
    print("a的内存地址:",id(a))
    print("b的内存地址:",id(b))
    

    输出:

    a的内存地址: 1896907216
    b的内存地址: 1896907216
    

    结论:未拷贝

  • 列表浅拷贝

    a = [1,2,3]
    b = copy.copy(a)
    print("a的内存地址:",id(a))
    print("b的内存地址:",id(b))
    

    输出:

    a的内存地址: 2140688273224
    b的内存地址: 2140681134216
    

    结论:拷贝成功,创建了新的内存地址

  • 列表嵌套情况浅拷贝(特别注意)

    a = [1,2,[3,4]]
    b = copy.copy(a)
    print("a:",a)
    print("a的内存地址:",id(a))
    print("b:",b)
    print("b的内存地址:",id(b))
    

    输出:

    a: [1, 2, [3, 4]]
    a的内存地址: 2140679959240
    b: [1, 2, [3, 4]]
    b的内存地址: 2140679959112
    

    外部地址改变,创建了新的内存地址

    验证内部嵌套列表是否拷贝:

    In [43]: id(a[2])
    Out[43]: 2140680066952
    
    In [44]: id(b[2])
    Out[44]: 2140680066952
    

    结论:嵌套列表内存地址相同,未进行拷贝

    再次验证,尝试修改a列表,是否对b列表造成影响:

    In [45]: a.append(5)
    In [46]: a[2].append("a")
    In [48]: b
        
    Out[48]: [1, 2, [3, 4, 'a']]
    

    分析:a列表修改外层列表不影响b列表;修改a列表的子列表会同时改变b列表的子列表。

    结论

    符合浅拷贝的特性,浅拷贝只会对可变类型第一层进行拷贝,而不会拷贝其可变类型的子对象,因此未拷贝部分指向的仍是同一个内存地址;

深拷贝

copy.deepcopy

deepcopy函数是深拷贝, 只要发现对象有可变类型就会对该对象到最后一个可变类型的每一层对象就行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储。

深拷贝同样无法拷贝不可变类型:字符串、数字、元组。

  • 不可变类型的深拷贝(主要讨论元组及其子元素)
# 不可变类型元组(需要特别注意)  
In [59]: a = (1,2)

In [60]: b = copy.deepcopy(a)

In [61]: id(a)
Out[61]: 2140688276424

In [62]: id(b)
Out[62]: 2140688276424
      
In [63]: a = (1,2,[1,3])

In [64]: b =copy.deepcopy(a)

    
# 此处虽然外层元组是不可变类型,但内存地址依然改变了,原因是深拷贝会对所有可变子对象进行拷贝,因此内部列表会被拷贝,内存地址改变
In [65]: id(a)
Out[65]: 2140685431720

In [66]: id(b)
Out[66]: 2140688106408
    
# 其内部的子列表被拷贝,内存地址改变,由于元组是不可变类型,内部改变,其本身地址也会改变。
In [67]: id(a[2])
Out[67]: 2140681195272

In [68]: id(b[2])
Out[68]: 2140687283336
# 其内部的不可变对象无法拷贝,内存地址不变
In [69]: id(a[1])
Out[69]: 1896906896

In [70]: id(b[1])
Out[70]: 1896906896
  

结论:

不可变类型进行深拷贝如果子对象没有可变类型则不会进行拷贝,而只 是拷贝了这个对象的引用,否则会对该对象到最后一个可变类型的每一层 对象就行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储

  • 可变类型的深拷贝(主要讨论列表及子列表)

    In [77]: a = [1,2,[1,2,3]]
    
    In [78]: b = copy.deepcopy(a)
    # 外部列表内存地址改变
    In [79]: id(a)
    Out[79]: 2140687234824
    
    In [80]: id(b)
    Out[80]: 2140681187208
    
    # 子列表内存地址也改变
    In [81]: id(a[2])
    Out[81]: 2140681025608
    
    In [82]: id(b[2])
    Out[82]: 2140690147272
        
    # 改变子列表元素不会再影响deepcoy的子列表元素
    In [83]: a[2].append(3)
    In [84]: a
    Out[84]: [1, 2, [1, 2, 3, 3]]
        
    In [85]: b
    Out[85]: [1, 2, [1, 2, 3]]
    
    

    结论:

    可变类型进行深拷贝会对该对象到最后一个可变类型的每一层对象就行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储。

总结

  • 浅拷贝使用copy.copy函数
  • 深拷贝使用copy.deepcopy函数
  • 不管是给对象进行深拷贝还是浅拷贝,只要拷贝成功就会开辟新的内存空间存储拷贝的对象。
  • 浅拷贝和深拷贝的区别是:
    • 浅拷贝最多拷贝对象的一层,深拷贝可能拷贝对象的多层。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,094评论 1 32
  • 1.设计模式是什么? 你知道哪些设计模式,并简要叙述?设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型...
    龍飝阅读 2,142评论 0 12
  • 一、GIL锁 1.1、GIL面试题:描述Python GIL的概念, 以及它对python多线程的影响?编写一个多...
    IIronMan阅读 449评论 0 0
  • 1. 引用类型有哪些?非引用类型有哪些 引用类型:对象、数组、函数、正则;指的是那些保存在堆内存中的对象,变量中保...
    曾祥辉阅读 215评论 0 0
  • 前世今生,一幕幕的在王飞的脑子里闪过,家破人亡,一切的一切究竟拜谁所赐。重生后他要让那些欺负他的人付出代价。...
    小孩子吃饭阅读 106评论 0 0